summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/bootstrap/CHANGELOG.md71
-rw-r--r--src/bootstrap/Cargo.lock181
-rw-r--r--src/bootstrap/Cargo.toml39
-rw-r--r--src/bootstrap/README.md14
-rw-r--r--src/bootstrap/bootstrap.py29
-rw-r--r--src/bootstrap/bootstrap_test.py3
-rwxr-xr-xsrc/bootstrap/configure.py20
-rw-r--r--src/bootstrap/defaults/config.codegen.toml2
-rw-r--r--src/bootstrap/download-ci-llvm-stamp2
-rw-r--r--src/bootstrap/job.rs143
-rw-r--r--src/bootstrap/src/bin/main.rs (renamed from src/bootstrap/bin/main.rs)59
-rw-r--r--src/bootstrap/src/bin/rustc.rs (renamed from src/bootstrap/bin/rustc.rs)72
-rw-r--r--src/bootstrap/src/bin/rustdoc.rs (renamed from src/bootstrap/bin/rustdoc.rs)16
-rw-r--r--src/bootstrap/src/bin/sccache-plus-cl.rs (renamed from src/bootstrap/bin/sccache-plus-cl.rs)0
-rw-r--r--src/bootstrap/src/core/build_steps/check.rs (renamed from src/bootstrap/check.rs)22
-rw-r--r--src/bootstrap/src/core/build_steps/clean.rs (renamed from src/bootstrap/clean.rs)15
-rw-r--r--src/bootstrap/src/core/build_steps/compile.rs (renamed from src/bootstrap/compile.rs)124
-rw-r--r--src/bootstrap/src/core/build_steps/dist.rs (renamed from src/bootstrap/dist.rs)202
-rw-r--r--src/bootstrap/src/core/build_steps/doc.rs (renamed from src/bootstrap/doc.rs)127
-rw-r--r--src/bootstrap/src/core/build_steps/format.rs (renamed from src/bootstrap/format.rs)6
-rw-r--r--src/bootstrap/src/core/build_steps/install.rs (renamed from src/bootstrap/install.rs)28
-rw-r--r--src/bootstrap/src/core/build_steps/llvm.rs (renamed from src/bootstrap/llvm.rs)43
-rw-r--r--src/bootstrap/src/core/build_steps/mod.rs15
-rw-r--r--src/bootstrap/src/core/build_steps/run.rs (renamed from src/bootstrap/run.rs)46
-rw-r--r--src/bootstrap/src/core/build_steps/setup.rs (renamed from src/bootstrap/setup.rs)80
-rw-r--r--src/bootstrap/src/core/build_steps/suggest.rs (renamed from src/bootstrap/suggest.rs)22
-rw-r--r--src/bootstrap/src/core/build_steps/synthetic_targets.rs (renamed from src/bootstrap/synthetic_targets.rs)6
-rw-r--r--src/bootstrap/src/core/build_steps/test.rs (renamed from src/bootstrap/test.rs)400
-rw-r--r--src/bootstrap/src/core/build_steps/tool.rs (renamed from src/bootstrap/tool.rs)25
-rw-r--r--src/bootstrap/src/core/build_steps/toolstate.rs (renamed from src/bootstrap/toolstate.rs)12
-rw-r--r--src/bootstrap/src/core/builder.rs (renamed from src/bootstrap/builder.rs)162
-rw-r--r--src/bootstrap/src/core/config/config.rs (renamed from src/bootstrap/config.rs)198
-rw-r--r--src/bootstrap/src/core/config/flags.rs (renamed from src/bootstrap/flags.rs)14
-rw-r--r--src/bootstrap/src/core/config/mod.rs4
-rw-r--r--src/bootstrap/src/core/download.rs (renamed from src/bootstrap/download.rs)63
-rw-r--r--src/bootstrap/src/core/metadata.rs (renamed from src/bootstrap/metadata.rs)4
-rw-r--r--src/bootstrap/src/core/mod.rs6
-rw-r--r--src/bootstrap/src/core/sanity.rs (renamed from src/bootstrap/sanity.rs)6
-rw-r--r--src/bootstrap/src/lib.rs (renamed from src/bootstrap/lib.rs)338
-rw-r--r--src/bootstrap/src/tests/builder.rs (renamed from src/bootstrap/builder/tests.rs)11
-rw-r--r--src/bootstrap/src/tests/config.rs (renamed from src/bootstrap/config/tests.rs)45
-rw-r--r--src/bootstrap/src/tests/setup.rs (renamed from src/bootstrap/setup/tests.rs)0
-rw-r--r--src/bootstrap/src/utils/bin_helpers.rs (renamed from src/bootstrap/bin/_helper.rs)14
-rw-r--r--src/bootstrap/src/utils/cache.rs (renamed from src/bootstrap/cache.rs)2
-rw-r--r--src/bootstrap/src/utils/cc_detect.rs (renamed from src/bootstrap/cc_detect.rs)83
-rw-r--r--src/bootstrap/src/utils/channel.rs (renamed from src/bootstrap/channel.rs)3
-rw-r--r--src/bootstrap/src/utils/dylib.rs (renamed from src/bootstrap/dylib_util.rs)11
-rw-r--r--src/bootstrap/src/utils/exec.rs60
-rw-r--r--src/bootstrap/src/utils/helpers.rs (renamed from src/bootstrap/util.rs)89
-rw-r--r--src/bootstrap/src/utils/job.rs159
-rw-r--r--src/bootstrap/src/utils/metrics.rs (renamed from src/bootstrap/metrics.rs)6
-rw-r--r--src/bootstrap/src/utils/mod.rs15
-rw-r--r--src/bootstrap/src/utils/render_tests.rs (renamed from src/bootstrap/render_tests.rs)4
-rw-r--r--src/bootstrap/src/utils/tarball.rs (renamed from src/bootstrap/tarball.rs)26
-rw-r--r--src/ci/docker/host-x86_64/arm-android/Dockerfile2
-rw-r--r--src/ci/docker/host-x86_64/dist-android/Dockerfile7
-rw-r--r--src/ci/docker/host-x86_64/dist-various-1/Dockerfile47
-rwxr-xr-xsrc/ci/docker/host-x86_64/dist-various-1/install-mips-musl.sh15
-rwxr-xr-xsrc/ci/docker/host-x86_64/dist-various-1/install-mipsel-musl.sh15
-rw-r--r--src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile7
-rwxr-xr-xsrc/ci/docker/host-x86_64/dist-x86_64-linux/build-clang.sh2
-rwxr-xr-xsrc/ci/docker/host-x86_64/dist-x86_64-linux/build-gcc.sh15
-rw-r--r--src/ci/docker/host-x86_64/test-various/uefi_qemu_test/Cargo.lock16
-rw-r--r--src/ci/docker/host-x86_64/wasm32/Dockerfile63
-rw-r--r--src/ci/docker/host-x86_64/x86_64-gnu-llvm-15/Dockerfile5
-rwxr-xr-xsrc/ci/docker/host-x86_64/x86_64-gnu-llvm-15/script.sh68
-rw-r--r--src/ci/docker/host-x86_64/x86_64-gnu-llvm-16/Dockerfile4
-rw-r--r--src/ci/docker/host-x86_64/x86_64-gnu-llvm-17/Dockerfile50
-rw-r--r--src/ci/docker/host-x86_64/x86_64-gnu-tools/Dockerfile11
-rwxr-xr-xsrc/ci/docker/host-x86_64/x86_64-gnu-tools/checktools.sh29
-rwxr-xr-xsrc/ci/docker/run.sh23
-rwxr-xr-xsrc/ci/docker/scripts/fuchsia-test-runner.py48
-rw-r--r--src/ci/github-actions/ci.yml83
-rwxr-xr-xsrc/ci/run.sh20
-rwxr-xr-xsrc/ci/scripts/install-awscli.sh29
-rwxr-xr-xsrc/ci/scripts/install-tidy.sh24
-rwxr-xr-xsrc/ci/scripts/verify-channel.sh2
-rw-r--r--src/doc/book/redirects/compiler-plugins.md11
-rw-r--r--src/doc/book/src/ch02-00-guessing-game-tutorial.md4
-rw-r--r--src/doc/embedded-book/src/start/hardware.md3
-rw-r--r--src/doc/guide-plugins.md3
-rw-r--r--src/doc/nomicon/src/exception-safety.md2
-rw-r--r--src/doc/reference/src/attributes.md4
-rw-r--r--src/doc/reference/src/attributes/codegen.md60
-rw-r--r--src/doc/reference/src/behavior-considered-undefined.md46
-rw-r--r--src/doc/reference/src/destructors.md6
-rw-r--r--src/doc/reference/src/expressions/operator-expr.md10
-rw-r--r--src/doc/reference/src/inline-assembly.md4
-rw-r--r--src/doc/reference/src/items/traits.md2
-rw-r--r--src/doc/reference/src/types/impl-trait.md10
-rw-r--r--src/doc/reference/src/types/textual.md4
-rw-r--r--src/doc/rust-by-example/src/SUMMARY.md4
-rw-r--r--src/doc/rust-by-example/src/attribute.md32
-rw-r--r--src/doc/rust-by-example/src/custom_types/constants.md2
-rw-r--r--src/doc/rust-by-example/src/error/option_unwrap/question_mark.md3
-rw-r--r--src/doc/rust-by-example/src/flow_control/while_let.md34
-rw-r--r--src/doc/rust-by-example/src/fn/closures.md4
-rw-r--r--src/doc/rust-by-example/src/fn/hof.md2
-rw-r--r--src/doc/rust-by-example/src/meta/doc.md9
-rw-r--r--src/doc/rust-by-example/src/meta/playground.md15
-rw-r--r--src/doc/rust-by-example/src/primitives/array.md2
-rw-r--r--src/doc/rust-by-example/src/scope/lifetime.md2
-rw-r--r--src/doc/rust-by-example/src/scope/move.md2
-rw-r--r--src/doc/rust-by-example/src/std/hash/alt_key_types.md2
-rw-r--r--src/doc/rust-by-example/src/std_misc/arg/matching.md3
-rw-r--r--src/doc/rust-by-example/src/std_misc/file/read_lines.md8
-rw-r--r--src/doc/rust-by-example/src/std_misc/threads/testcase_mapreduce.md2
-rw-r--r--src/doc/rustc-dev-guide/examples/rustc-driver-example.rs2
-rw-r--r--src/doc/rustc-dev-guide/examples/rustc-driver-getting-diagnostics.rs2
-rw-r--r--src/doc/rustc-dev-guide/examples/rustc-driver-interacting-with-the-ast.rs2
-rw-r--r--src/doc/rustc-dev-guide/src/SUMMARY.md2
-rw-r--r--src/doc/rustc-dev-guide/src/appendix/bibliography.md2
-rw-r--r--src/doc/rustc-dev-guide/src/appendix/glossary.md2
-rw-r--r--src/doc/rustc-dev-guide/src/building/suggested.md1
-rw-r--r--src/doc/rustc-dev-guide/src/effects.md66
-rw-r--r--src/doc/rustc-dev-guide/src/feature-gates.md12
-rw-r--r--src/doc/rustc-dev-guide/src/hir-debugging.md9
-rw-r--r--src/doc/rustc-dev-guide/src/implementing_new_features.md6
-rw-r--r--src/doc/rustc-dev-guide/src/llvm-coverage-instrumentation.md123
-rw-r--r--src/doc/rustc-dev-guide/src/return-position-impl-trait-in-trait.md18
-rw-r--r--src/doc/rustc-dev-guide/src/rustc-driver-getting-diagnostics.md2
-rw-r--r--src/doc/rustc-dev-guide/src/rustc-driver-interacting-with-the-ast.md2
-rw-r--r--src/doc/rustc-dev-guide/src/solve/invariants.md154
-rw-r--r--src/doc/rustc-dev-guide/src/solve/the-solver.md73
-rw-r--r--src/doc/rustc-dev-guide/src/solve/trait-solving.md71
-rw-r--r--src/doc/rustc-dev-guide/src/stabilization_guide.md2
-rw-r--r--src/doc/rustc-dev-guide/src/tests/headers.md2
-rw-r--r--src/doc/rustc-dev-guide/src/traits/unsize.md84
-rw-r--r--src/doc/rustc/src/SUMMARY.md3
-rw-r--r--src/doc/rustc/src/codegen-options/index.md8
-rw-r--r--src/doc/rustc/src/exploit-mitigations.md404
-rw-r--r--src/doc/rustc/src/images/image1.pngbin15293 -> 164896 bytes
-rw-r--r--src/doc/rustc/src/images/image2.pngbin28772 -> 155307 bytes
-rw-r--r--src/doc/rustc/src/images/image3.pngbin19069 -> 19936 bytes
-rw-r--r--src/doc/rustc/src/platform-support.md73
-rw-r--r--src/doc/rustc/src/platform-support/aarch64-unknown-teeos.md2
-rw-r--r--src/doc/rustc/src/platform-support/aix.md26
-rw-r--r--src/doc/rustc/src/platform-support/android.md16
-rw-r--r--src/doc/rustc/src/platform-support/apple-tvos.md4
-rw-r--r--src/doc/rustc/src/platform-support/armv7-sony-vita-newlibeabihf.md137
-rw-r--r--src/doc/rustc/src/platform-support/csky-unknown-linux-gnuabiv2.md24
-rw-r--r--src/doc/rustc/src/platform-support/fuchsia.md4
-rw-r--r--src/doc/rustc/src/platform-support/mips-release-6.md2
-rw-r--r--src/doc/rustc/src/platform-support/nto-qnx.md2
-rw-r--r--src/doc/rustc/src/platform-support/openharmony.md2
-rw-r--r--src/doc/rustc/src/platform-support/unknown-uefi.md10
-rw-r--r--src/doc/rustc/src/profile-guided-optimization.md23
-rw-r--r--src/doc/rustdoc/src/advanced-features.md20
-rw-r--r--src/doc/rustdoc/src/unstable-features.md19
-rw-r--r--src/doc/rustdoc/src/write-documentation/what-to-include.md4
-rw-r--r--src/doc/unstable-book/src/compiler-flags/check-cfg.md216
-rw-r--r--src/doc/unstable-book/src/compiler-flags/no-jump-tables.md19
-rw-r--r--src/doc/unstable-book/src/compiler-flags/remap-path-scope.md24
-rw-r--r--src/doc/unstable-book/src/compiler-flags/sanitizer.md161
-rw-r--r--src/doc/unstable-book/src/language-features/closure-track-caller.md4
-rw-r--r--src/doc/unstable-book/src/language-features/coroutines.md246
-rw-r--r--src/doc/unstable-book/src/language-features/diagnostic-namespace.md84
-rw-r--r--src/doc/unstable-book/src/language-features/generators.md246
-rw-r--r--src/doc/unstable-book/src/language-features/plugin.md114
-rw-r--r--src/doc/unstable-book/src/language-features/string-deref-patterns.md45
-rw-r--r--src/doc/unstable-book/src/the-unstable-book.md18
-rw-r--r--src/etc/completions/x.py.fish120
-rw-r--r--src/etc/completions/x.py.ps138
-rw-r--r--src/etc/completions/x.py.sh32
-rw-r--r--src/etc/completions/x.py.zsh766
-rw-r--r--src/etc/gdb_lookup.py133
-rw-r--r--src/etc/gdb_providers.py307
-rw-r--r--src/etc/test-float-parse/Cargo.lock75
-rw-r--r--src/etc/test-float-parse/Cargo.toml2
-rw-r--r--src/librustdoc/Cargo.toml3
-rw-r--r--src/librustdoc/clean/auto_trait.rs7
-rw-r--r--src/librustdoc/clean/inline.rs37
-rw-r--r--src/librustdoc/clean/mod.rs209
-rw-r--r--src/librustdoc/clean/simplify.rs21
-rw-r--r--src/librustdoc/clean/types.rs98
-rw-r--r--src/librustdoc/clean/types/tests.rs3
-rw-r--r--src/librustdoc/clean/utils.rs164
-rw-r--r--src/librustdoc/core.rs17
-rw-r--r--src/librustdoc/doctest.rs13
-rw-r--r--src/librustdoc/formats/cache.rs86
-rw-r--r--src/librustdoc/formats/item_type.rs5
-rw-r--r--src/librustdoc/html/format.rs80
-rw-r--r--src/librustdoc/html/layout.rs37
-rw-r--r--src/librustdoc/html/markdown.rs5
-rw-r--r--src/librustdoc/html/render/context.rs75
-rw-r--r--src/librustdoc/html/render/mod.rs140
-rw-r--r--src/librustdoc/html/render/print_item.rs274
-rw-r--r--src/librustdoc/html/render/search_index.rs33
-rw-r--r--src/librustdoc/html/render/sidebar.rs105
-rw-r--r--src/librustdoc/html/render/write_shared.rs289
-rw-r--r--src/librustdoc/html/sources.rs7
-rw-r--r--src/librustdoc/html/static/css/rustdoc.css87
-rw-r--r--src/librustdoc/html/static/js/main.js281
-rw-r--r--src/librustdoc/html/static/js/search.js21
-rw-r--r--src/librustdoc/html/templates/page.html52
-rw-r--r--src/librustdoc/html/templates/sidebar.html7
-rw-r--r--src/librustdoc/json/conversions.rs28
-rw-r--r--src/librustdoc/lib.rs15
-rw-r--r--src/librustdoc/markdown.rs2
-rw-r--r--src/librustdoc/passes/collect_intra_doc_links.rs6
-rw-r--r--src/librustdoc/passes/collect_trait_impls.rs20
-rw-r--r--src/librustdoc/passes/lint/html_tags.rs150
-rw-r--r--src/librustdoc/passes/lint/redundant_explicit_links.rs2
-rw-r--r--src/stage0.json553
-rw-r--r--src/tools/build-manifest/src/main.rs40
-rw-r--r--src/tools/build-manifest/src/versions.rs3
-rw-r--r--src/tools/build_helper/src/ci.rs6
-rw-r--r--src/tools/build_helper/src/git.rs41
-rw-r--r--src/tools/cargo/.github/renovate.json540
-rw-r--r--src/tools/cargo/.github/workflows/audit.yml2
-rw-r--r--src/tools/cargo/.github/workflows/contrib.yml26
-rw-r--r--src/tools/cargo/.github/workflows/main.yml34
-rw-r--r--src/tools/cargo/CHANGELOG.md219
-rw-r--r--src/tools/cargo/CONTRIBUTING.md10
-rw-r--r--src/tools/cargo/Cargo.lock470
-rw-r--r--src/tools/cargo/Cargo.toml87
-rw-r--r--src/tools/cargo/ci/generate.py49
-rw-r--r--src/tools/cargo/crates/cargo-platform/Cargo.toml3
-rw-r--r--src/tools/cargo/crates/cargo-test-support/src/compare.rs2
-rw-r--r--src/tools/cargo/crates/cargo-test-support/src/diff.rs2
-rw-r--r--src/tools/cargo/crates/cargo-test-support/src/lib.rs48
-rw-r--r--src/tools/cargo/crates/cargo-util/Cargo.toml4
-rw-r--r--src/tools/cargo/crates/cargo-util/src/paths.rs39
-rw-r--r--src/tools/cargo/crates/crates-io/Cargo.toml2
-rw-r--r--src/tools/cargo/crates/crates-io/lib.rs4
-rw-r--r--src/tools/cargo/crates/home/Cargo.toml3
-rw-r--r--src/tools/cargo/crates/home/src/lib.rs1
-rw-r--r--src/tools/cargo/crates/resolver-tests/src/lib.rs21
-rw-r--r--src/tools/cargo/crates/xtask-bump-check/Cargo.toml3
-rw-r--r--src/tools/cargo/crates/xtask-bump-check/src/xtask.rs24
-rw-r--r--src/tools/cargo/credential/cargo-credential-1password/Cargo.toml3
l---------src/tools/cargo/credential/cargo-credential-1password/LICENSE-APACHE1
l---------src/tools/cargo/credential/cargo-credential-1password/LICENSE-MIT1
-rw-r--r--src/tools/cargo/credential/cargo-credential-1password/README.md38
-rw-r--r--src/tools/cargo/credential/cargo-credential-1password/src/main.rs4
-rw-r--r--src/tools/cargo/credential/cargo-credential-libsecret/Cargo.toml3
l---------src/tools/cargo/credential/cargo-credential-libsecret/LICENSE-APACHE1
l---------src/tools/cargo/credential/cargo-credential-libsecret/LICENSE-MIT1
-rw-r--r--src/tools/cargo/credential/cargo-credential-macos-keychain/Cargo.toml3
l---------src/tools/cargo/credential/cargo-credential-macos-keychain/LICENSE-APACHE1
l---------src/tools/cargo/credential/cargo-credential-macos-keychain/LICENSE-MIT1
-rw-r--r--src/tools/cargo/credential/cargo-credential-wincred/Cargo.toml3
l---------src/tools/cargo/credential/cargo-credential-wincred/LICENSE-APACHE1
l---------src/tools/cargo/credential/cargo-credential-wincred/LICENSE-MIT1
-rw-r--r--src/tools/cargo/credential/cargo-credential/Cargo.toml4
l---------src/tools/cargo/credential/cargo-credential/LICENSE-APACHE1
l---------src/tools/cargo/credential/cargo-credential/LICENSE-MIT1
-rw-r--r--src/tools/cargo/src/bin/cargo/cli.rs32
-rw-r--r--src/tools/cargo/src/bin/cargo/commands/add.rs2
-rw-r--r--src/tools/cargo/src/bin/cargo/commands/bench.rs5
-rw-r--r--src/tools/cargo/src/bin/cargo/commands/build.rs13
-rw-r--r--src/tools/cargo/src/bin/cargo/commands/check.rs4
-rw-r--r--src/tools/cargo/src/bin/cargo/commands/fix.rs4
-rw-r--r--src/tools/cargo/src/bin/cargo/commands/init.rs7
-rw-r--r--src/tools/cargo/src/bin/cargo/commands/install.rs17
-rw-r--r--src/tools/cargo/src/bin/cargo/commands/login.rs2
-rw-r--r--src/tools/cargo/src/bin/cargo/commands/new.rs7
-rw-r--r--src/tools/cargo/src/bin/cargo/commands/owner.rs2
-rw-r--r--src/tools/cargo/src/bin/cargo/commands/pkgid.rs2
-rw-r--r--src/tools/cargo/src/bin/cargo/commands/remove.rs13
-rw-r--r--src/tools/cargo/src/bin/cargo/commands/run.rs1
-rw-r--r--src/tools/cargo/src/bin/cargo/commands/rustc.rs5
-rw-r--r--src/tools/cargo/src/bin/cargo/commands/rustdoc.rs5
-rw-r--r--src/tools/cargo/src/bin/cargo/commands/search.rs2
-rw-r--r--src/tools/cargo/src/bin/cargo/commands/test.rs5
-rw-r--r--src/tools/cargo/src/bin/cargo/commands/uninstall.rs2
-rw-r--r--src/tools/cargo/src/bin/cargo/commands/yank.rs2
-rw-r--r--src/tools/cargo/src/bin/cargo/main.rs9
-rw-r--r--src/tools/cargo/src/cargo/core/compiler/context/mod.rs8
-rw-r--r--src/tools/cargo/src/cargo/core/compiler/custom_build.rs20
-rw-r--r--src/tools/cargo/src/cargo/core/compiler/future_incompat.rs10
-rw-r--r--src/tools/cargo/src/cargo/core/compiler/job_queue/mod.rs5
-rw-r--r--src/tools/cargo/src/cargo/core/compiler/layout.rs2
-rw-r--r--src/tools/cargo/src/cargo/core/compiler/mod.rs129
-rw-r--r--src/tools/cargo/src/cargo/core/compiler/timings.rs32
-rw-r--r--src/tools/cargo/src/cargo/core/dependency.rs4
-rw-r--r--src/tools/cargo/src/cargo/core/features.rs82
-rw-r--r--src/tools/cargo/src/cargo/core/manifest.rs2
-rw-r--r--src/tools/cargo/src/cargo/core/mod.rs2
-rw-r--r--src/tools/cargo/src/cargo/core/package.rs11
-rw-r--r--src/tools/cargo/src/cargo/core/package_id.rs85
-rw-r--r--src/tools/cargo/src/cargo/core/package_id_spec.rs162
-rw-r--r--src/tools/cargo/src/cargo/core/profiles.rs70
-rw-r--r--src/tools/cargo/src/cargo/core/registry.rs10
-rw-r--r--src/tools/cargo/src/cargo/core/resolver/context.rs4
-rw-r--r--src/tools/cargo/src/cargo/core/resolver/dep_cache.rs77
-rw-r--r--src/tools/cargo/src/cargo/core/resolver/encode.rs2
-rw-r--r--src/tools/cargo/src/cargo/core/resolver/errors.rs3
-rw-r--r--src/tools/cargo/src/cargo/core/resolver/mod.rs54
-rw-r--r--src/tools/cargo/src/cargo/core/resolver/resolve.rs11
-rw-r--r--src/tools/cargo/src/cargo/core/resolver/version_prefs.rs126
-rw-r--r--src/tools/cargo/src/cargo/core/shell.rs137
-rw-r--r--src/tools/cargo/src/cargo/core/source_id.rs244
-rw-r--r--src/tools/cargo/src/cargo/core/summary.rs6
-rw-r--r--src/tools/cargo/src/cargo/core/workspace.rs6
-rw-r--r--src/tools/cargo/src/cargo/lib.rs3
-rw-r--r--src/tools/cargo/src/cargo/ops/cargo_add/mod.rs47
-rw-r--r--src/tools/cargo/src/cargo/ops/cargo_compile/mod.rs2
-rw-r--r--src/tools/cargo/src/cargo/ops/cargo_doc.rs24
-rw-r--r--src/tools/cargo/src/cargo/ops/cargo_generate_lockfile.rs22
-rw-r--r--src/tools/cargo/src/cargo/ops/cargo_install.rs42
-rw-r--r--src/tools/cargo/src/cargo/ops/cargo_new.rs113
-rw-r--r--src/tools/cargo/src/cargo/ops/cargo_package.rs20
-rw-r--r--src/tools/cargo/src/cargo/ops/cargo_uninstall.rs1
-rw-r--r--src/tools/cargo/src/cargo/ops/common_for_install_and_uninstall.rs70
-rw-r--r--src/tools/cargo/src/cargo/ops/fix.rs5
-rw-r--r--src/tools/cargo/src/cargo/ops/lockfile.rs23
-rw-r--r--src/tools/cargo/src/cargo/ops/registry/mod.rs3
-rw-r--r--src/tools/cargo/src/cargo/ops/registry/publish.rs5
-rw-r--r--src/tools/cargo/src/cargo/ops/registry/search.rs39
-rw-r--r--src/tools/cargo/src/cargo/ops/resolve.rs24
-rw-r--r--src/tools/cargo/src/cargo/ops/vendor.rs2
-rw-r--r--src/tools/cargo/src/cargo/sources/config.rs6
-rw-r--r--src/tools/cargo/src/cargo/sources/git/source.rs17
-rw-r--r--src/tools/cargo/src/cargo/sources/registry/download.rs7
-rw-r--r--src/tools/cargo/src/cargo/sources/registry/http_remote.rs5
-rw-r--r--src/tools/cargo/src/cargo/sources/registry/index.rs87
-rw-r--r--src/tools/cargo/src/cargo/sources/registry/mod.rs71
-rw-r--r--src/tools/cargo/src/cargo/sources/registry/remote.rs15
-rw-r--r--src/tools/cargo/src/cargo/util/auth/mod.rs6
-rw-r--r--src/tools/cargo/src/cargo/util/cache_lock.rs549
-rw-r--r--src/tools/cargo/src/cargo/util/command_prelude.rs70
-rw-r--r--src/tools/cargo/src/cargo/util/config/mod.rs135
-rw-r--r--src/tools/cargo/src/cargo/util/config/target.rs8
-rw-r--r--src/tools/cargo/src/cargo/util/flock.rs287
-rw-r--r--src/tools/cargo/src/cargo/util/hostname.rs77
-rw-r--r--src/tools/cargo/src/cargo/util/mod.rs7
-rw-r--r--src/tools/cargo/src/cargo/util/restricted_names.rs79
-rw-r--r--src/tools/cargo/src/cargo/util/rustc.rs14
-rw-r--r--src/tools/cargo/src/cargo/util/semver_ext.rs293
-rw-r--r--src/tools/cargo/src/cargo/util/to_semver.rs36
-rw-r--r--src/tools/cargo/src/cargo/util/toml/embedded.rs16
-rw-r--r--src/tools/cargo/src/cargo/util/toml/mod.rs2495
-rw-r--r--src/tools/cargo/src/cargo/util/toml/schema.rs1189
-rw-r--r--src/tools/cargo/src/cargo/util/toml/targets.rs120
-rw-r--r--src/tools/cargo/src/cargo/util/toml_mut/dependency.rs45
-rw-r--r--src/tools/cargo/src/cargo/util/toml_mut/manifest.rs100
-rw-r--r--src/tools/cargo/src/cargo/util/toml_mut/mod.rs16
-rw-r--r--src/tools/cargo/src/cargo/util/workspace.rs4
-rw-r--r--src/tools/cargo/src/cargo/util_semver.rs195
-rw-r--r--src/tools/cargo/src/doc/contrib/book.toml2
-rw-r--r--src/tools/cargo/src/doc/contrib/src/SUMMARY.md2
-rw-r--r--src/tools/cargo/src/doc/contrib/src/implementation/formatting.md17
-rw-r--r--src/tools/cargo/src/doc/contrib/src/implementation/packages.md52
-rw-r--r--src/tools/cargo/src/doc/contrib/src/process/index.md9
-rw-r--r--src/tools/cargo/src/doc/contrib/src/process/working-on-cargo.md9
-rw-r--r--src/tools/cargo/src/doc/man/cargo-add.md2
-rw-r--r--src/tools/cargo/src/doc/man/cargo-bench.md2
-rw-r--r--src/tools/cargo/src/doc/man/cargo-install.md2
-rw-r--r--src/tools/cargo/src/doc/man/cargo-login.md4
-rw-r--r--src/tools/cargo/src/doc/man/cargo-rustc.md2
-rw-r--r--src/tools/cargo/src/doc/man/cargo-vendor.md10
-rw-r--r--src/tools/cargo/src/doc/man/generated_txt/cargo-add.txt4
-rw-r--r--src/tools/cargo/src/doc/man/generated_txt/cargo-bench.txt5
-rw-r--r--src/tools/cargo/src/doc/man/generated_txt/cargo-build.txt2
-rw-r--r--src/tools/cargo/src/doc/man/generated_txt/cargo-check.txt2
-rw-r--r--src/tools/cargo/src/doc/man/generated_txt/cargo-doc.txt2
-rw-r--r--src/tools/cargo/src/doc/man/generated_txt/cargo-fix.txt2
-rw-r--r--src/tools/cargo/src/doc/man/generated_txt/cargo-init.txt2
-rw-r--r--src/tools/cargo/src/doc/man/generated_txt/cargo-install.txt4
-rw-r--r--src/tools/cargo/src/doc/man/generated_txt/cargo-login.txt5
-rw-r--r--src/tools/cargo/src/doc/man/generated_txt/cargo-new.txt2
-rw-r--r--src/tools/cargo/src/doc/man/generated_txt/cargo-run.txt2
-rw-r--r--src/tools/cargo/src/doc/man/generated_txt/cargo-rustc.txt2
-rw-r--r--src/tools/cargo/src/doc/man/generated_txt/cargo-rustdoc.txt2
-rw-r--r--src/tools/cargo/src/doc/man/generated_txt/cargo-test.txt2
-rw-r--r--src/tools/cargo/src/doc/man/generated_txt/cargo-vendor.txt11
-rw-r--r--src/tools/cargo/src/doc/man/includes/options-new.md2
-rw-r--r--src/tools/cargo/src/doc/man/includes/options-profile-legacy-check.md2
-rw-r--r--src/tools/cargo/src/doc/man/includes/options-profile.md2
-rw-r--r--src/tools/cargo/src/doc/src/commands/cargo-add.md2
-rw-r--r--src/tools/cargo/src/doc/src/commands/cargo-bench.md4
-rw-r--r--src/tools/cargo/src/doc/src/commands/cargo-build.md2
-rw-r--r--src/tools/cargo/src/doc/src/commands/cargo-check.md2
-rw-r--r--src/tools/cargo/src/doc/src/commands/cargo-doc.md2
-rw-r--r--src/tools/cargo/src/doc/src/commands/cargo-fix.md2
-rw-r--r--src/tools/cargo/src/doc/src/commands/cargo-init.md2
-rw-r--r--src/tools/cargo/src/doc/src/commands/cargo-install.md4
-rw-r--r--src/tools/cargo/src/doc/src/commands/cargo-login.md4
-rw-r--r--src/tools/cargo/src/doc/src/commands/cargo-new.md2
-rw-r--r--src/tools/cargo/src/doc/src/commands/cargo-run.md2
-rw-r--r--src/tools/cargo/src/doc/src/commands/cargo-rustc.md2
-rw-r--r--src/tools/cargo/src/doc/src/commands/cargo-rustdoc.md2
-rw-r--r--src/tools/cargo/src/doc/src/commands/cargo-test.md2
-rw-r--r--src/tools/cargo/src/doc/src/commands/cargo-vendor.md10
-rw-r--r--src/tools/cargo/src/doc/src/reference/config.md23
-rw-r--r--src/tools/cargo/src/doc/src/reference/environment-variables.md2
-rw-r--r--src/tools/cargo/src/doc/src/reference/features.md7
-rw-r--r--src/tools/cargo/src/doc/src/reference/manifest.md35
-rw-r--r--src/tools/cargo/src/doc/src/reference/profiles.md4
-rw-r--r--src/tools/cargo/src/doc/src/reference/publishing.md2
-rw-r--r--src/tools/cargo/src/doc/src/reference/registry-authentication.md2
-rw-r--r--src/tools/cargo/src/doc/src/reference/resolver.md47
-rw-r--r--src/tools/cargo/src/doc/src/reference/specifying-dependencies.md4
-rw-r--r--src/tools/cargo/src/doc/src/reference/unstable.md172
-rw-r--r--src/tools/cargo/src/etc/man/cargo-add.12
-rw-r--r--src/tools/cargo/src/etc/man/cargo-bench.14
-rw-r--r--src/tools/cargo/src/etc/man/cargo-build.12
-rw-r--r--src/tools/cargo/src/etc/man/cargo-check.12
-rw-r--r--src/tools/cargo/src/etc/man/cargo-doc.12
-rw-r--r--src/tools/cargo/src/etc/man/cargo-fix.12
-rw-r--r--src/tools/cargo/src/etc/man/cargo-init.12
-rw-r--r--src/tools/cargo/src/etc/man/cargo-install.14
-rw-r--r--src/tools/cargo/src/etc/man/cargo-login.14
-rw-r--r--src/tools/cargo/src/etc/man/cargo-new.12
-rw-r--r--src/tools/cargo/src/etc/man/cargo-run.12
-rw-r--r--src/tools/cargo/src/etc/man/cargo-rustc.12
-rw-r--r--src/tools/cargo/src/etc/man/cargo-rustdoc.12
-rw-r--r--src/tools/cargo/src/etc/man/cargo-test.12
-rw-r--r--src/tools/cargo/src/etc/man/cargo-vendor.116
-rw-r--r--src/tools/cargo/tests/testsuite/artifact_dep.rs2
-rw-r--r--src/tools/cargo/tests/testsuite/bad_config.rs36
-rw-r--r--src/tools/cargo/tests/testsuite/build.rs137
-rw-r--r--src/tools/cargo/tests/testsuite/build_script.rs11
-rw-r--r--src/tools/cargo/tests/testsuite/build_script_env.rs22
-rw-r--r--src/tools/cargo/tests/testsuite/build_script_extra_link_arg.rs2
-rw-r--r--src/tools/cargo/tests/testsuite/cache_lock.rs304
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/mod.rs1
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/preserve_dep_std_table/in/Cargo.toml14
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/preserve_dep_std_table/in/src/lib.rs (renamed from src/tools/cargo/tests/testsuite/cargo_init/empty_dir/.keep)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/preserve_dep_std_table/mod.rs31
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/preserve_dep_std_table/out/Cargo.toml12
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/preserve_dep_std_table/stderr.log7
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/preserve_dep_std_table/stdout.log0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_bench/help/stdout.log8
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_bench/no_keep_going/stderr.log2
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_build/help/stdout.log4
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_check/help/stdout.log4
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_command.rs12
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_features.rs8
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_fix/help/stdout.log4
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/help/stdout.log6
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/mod.rs1
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/unknown_flags/stderr.log2
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/workspace_add_member/in/Cargo.toml2
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/workspace_add_member/in/crates/foo/.keep0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/workspace_add_member/mod.rs21
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/workspace_add_member/out/Cargo.toml3
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/workspace_add_member/out/crates/foo/Cargo.toml8
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/workspace_add_member/out/crates/foo/src/main.rs3
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/workspace_add_member/stderr.log1
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/workspace_add_member/stdout.log0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_install/help/stdout.log4
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_login/help/stdout.log4
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_previous_items/in/Cargo.toml5
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_previous_items/mod.rs22
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_previous_items/out/Cargo.toml6
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_previous_items/out/crates/foo/Cargo.toml9
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_previous_items/out/crates/foo/src/main.rs3
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_previous_items/stderr.log1
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_previous_items/stdout.log0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_sorted/in/Cargo.toml3
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_sorted/in/crates/bar/Cargo.toml8
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_sorted/in/crates/bar/src/main.rs3
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_sorted/in/crates/qux/Cargo.toml8
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_sorted/in/crates/qux/src/main.rs3
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_sorted/mod.rs22
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_sorted/out/Cargo.toml3
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_sorted/out/crates/foo/Cargo.toml8
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_sorted/out/crates/foo/src/main.rs3
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_sorted/stderr.log1
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_sorted/stdout.log0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_absolute_package_path/in/Cargo.toml3
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_absolute_package_path/mod.rs23
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_absolute_package_path/out/Cargo.toml3
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_absolute_package_path/out/crates/foo/Cargo.toml8
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_absolute_package_path/out/crates/foo/src/main.rs3
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_absolute_package_path/stderr.log1
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_absolute_package_path/stdout.log0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_empty_members/in/Cargo.toml3
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_empty_members/mod.rs22
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_empty_members/out/Cargo.toml3
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_empty_members/out/crates/foo/Cargo.toml8
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_empty_members/out/crates/foo/src/main.rs3
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_empty_members/stderr.log1
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_empty_members/stdout.log0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_exclude_list/in/Cargo.toml4
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_exclude_list/in/src/lib.rs14
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_exclude_list/mod.rs22
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_exclude_list/out/Cargo.toml4
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_exclude_list/out/crates/foo/Cargo.toml8
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_exclude_list/out/crates/foo/src/main.rs3
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_exclude_list/stderr.log1
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_exclude_list/stdout.log0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_members_glob/in/Cargo.toml3
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_members_glob/mod.rs22
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_members_glob/out/Cargo.toml3
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_members_glob/out/crates/foo/Cargo.toml8
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_members_glob/out/crates/foo/src/main.rs3
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_members_glob/stderr.log1
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_members_glob/stdout.log0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/help/stdout.log6
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/mod.rs6
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/not_inherit_workspace_package_table_if_not_members/out/Cargo.toml2
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/not_inherit_workspace_package_table_if_not_members/stderr.log8
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_owner/help/stdout.log4
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_pkgid/help/stdout.log4
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_remove/help/stdout.log6
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_remove/invalid_dep/stderr.log2
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_remove/invalid_section/stderr.log2
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_remove/invalid_section_dep/stderr.log2
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_remove/invalid_target/stderr.log2
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_remove/invalid_target_dep/stderr.log2
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_remove/mod.rs1
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_remove/multiple_dev/out/Cargo.toml2
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_remove/optional_dep_feature/out/Cargo.toml2
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_remove/optional_dep_feature_formatting/in/Cargo.toml42
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_remove/optional_dep_feature_formatting/in/src/lib.rs1
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_remove/optional_dep_feature_formatting/mod.rs35
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_remove/optional_dep_feature_formatting/out/Cargo.toml40
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_remove/optional_dep_feature_formatting/stderr.log2
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_remove/optional_dep_feature_formatting/stdout.log0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_run/help/stdout.log4
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_rustc/help/stdout.log8
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_rustdoc/help/stdout.log8
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_search/help/stdout.log4
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_test/help/stdout.log8
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_test/no_keep_going/stderr.log2
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_uninstall/help/stdout.log4
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_yank/help/stdout.log4
-rw-r--r--src/tools/cargo/tests/testsuite/check.rs23
-rw-r--r--src/tools/cargo/tests/testsuite/check_cfg.rs307
-rw-r--r--src/tools/cargo/tests/testsuite/collisions.rs6
-rw-r--r--src/tools/cargo/tests/testsuite/config.rs109
-rw-r--r--src/tools/cargo/tests/testsuite/cross_compile.rs86
-rw-r--r--src/tools/cargo/tests/testsuite/custom_target.rs2
-rw-r--r--src/tools/cargo/tests/testsuite/death.rs153
-rw-r--r--src/tools/cargo/tests/testsuite/doc.rs46
-rw-r--r--src/tools/cargo/tests/testsuite/docscrape.rs33
-rw-r--r--src/tools/cargo/tests/testsuite/features.rs88
-rw-r--r--src/tools/cargo/tests/testsuite/features2.rs5
-rw-r--r--src/tools/cargo/tests/testsuite/glob_targets.rs5
-rw-r--r--src/tools/cargo/tests/testsuite/install.rs50
-rw-r--r--src/tools/cargo/tests/testsuite/install_upgrade.rs2
-rw-r--r--src/tools/cargo/tests/testsuite/list_availables.rs8
-rw-r--r--src/tools/cargo/tests/testsuite/main.rs3
-rw-r--r--src/tools/cargo/tests/testsuite/metadata.rs282
-rw-r--r--src/tools/cargo/tests/testsuite/multitarget.rs28
-rw-r--r--src/tools/cargo/tests/testsuite/new.rs26
-rw-r--r--src/tools/cargo/tests/testsuite/out_dir.rs23
-rw-r--r--src/tools/cargo/tests/testsuite/package.rs41
-rw-r--r--src/tools/cargo/tests/testsuite/plugins.rs421
-rw-r--r--src/tools/cargo/tests/testsuite/proc_macro.rs46
-rw-r--r--src/tools/cargo/tests/testsuite/profile_config.rs2
-rw-r--r--src/tools/cargo/tests/testsuite/profile_targets.rs1
-rw-r--r--src/tools/cargo/tests/testsuite/profile_trim_paths.rs614
-rw-r--r--src/tools/cargo/tests/testsuite/pub_priv.rs49
-rw-r--r--src/tools/cargo/tests/testsuite/publish.rs35
-rw-r--r--src/tools/cargo/tests/testsuite/registry.rs51
-rw-r--r--src/tools/cargo/tests/testsuite/run.rs4
-rw-r--r--src/tools/cargo/tests/testsuite/rustdoc.rs8
-rw-r--r--src/tools/cargo/tests/testsuite/rustdocflags.rs6
-rw-r--r--src/tools/cargo/tests/testsuite/script.rs1
-rw-r--r--src/tools/cargo/tests/testsuite/search.rs5
-rw-r--r--src/tools/cargo/tests/testsuite/update.rs98
-rw-r--r--src/tools/cargo/tests/testsuite/version.rs4
-rw-r--r--src/tools/cargo/tests/testsuite/warn_on_failure.rs4
-rw-r--r--src/tools/cargo/tests/testsuite/workspaces.rs17
-rw-r--r--src/tools/cargo/triagebot.toml10
-rw-r--r--src/tools/clippy/.github/workflows/clippy.yml2
-rw-r--r--src/tools/clippy/.github/workflows/clippy_bors.yml6
-rw-r--r--src/tools/clippy/CHANGELOG.md102
-rw-r--r--src/tools/clippy/Cargo.toml7
-rw-r--r--src/tools/clippy/book/src/development/adding_lints.md21
-rw-r--r--src/tools/clippy/book/src/lint_configuration.md152
-rw-r--r--src/tools/clippy/clippy_config/Cargo.toml21
-rw-r--r--src/tools/clippy/clippy_config/src/conf.rs (renamed from src/tools/clippy/clippy_lints/src/utils/conf.rs)319
-rw-r--r--src/tools/clippy/clippy_config/src/lib.rs23
-rw-r--r--src/tools/clippy/clippy_config/src/metadata.rs116
-rw-r--r--src/tools/clippy/clippy_config/src/msrvs.rs (renamed from src/tools/clippy/clippy_utils/src/msrvs.rs)106
-rw-r--r--src/tools/clippy/clippy_config/src/types.rs142
-rw-r--r--src/tools/clippy/clippy_dev/src/main.rs1
-rw-r--r--src/tools/clippy/clippy_dev/src/new_lint.rs67
-rw-r--r--src/tools/clippy/clippy_dev/src/update_lints.rs2
-rw-r--r--src/tools/clippy/clippy_lints/Cargo.toml10
-rw-r--r--src/tools/clippy/clippy_lints/src/absolute_paths.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/allow_attributes.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/almost_complete_range.rs31
-rw-r--r--src/tools/clippy/clippy_lints/src/approx_const.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/arc_with_non_send_sync.rs26
-rw-r--r--src/tools/clippy/clippy_lints/src/as_conversions.rs9
-rw-r--r--src/tools/clippy/clippy_lints/src/assertions_on_result_states.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/async_yields_async.rs10
-rw-r--r--src/tools/clippy/clippy_lints/src/attrs.rs88
-rw-r--r--src/tools/clippy/clippy_lints/src/await_holding_invalid.rs38
-rw-r--r--src/tools/clippy/clippy_lints/src/blocks_in_if_conditions.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/bool_assert_comparison.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/bool_to_int_with_if.rs31
-rw-r--r--src/tools/clippy/clippy_lints/src/booleans.rs16
-rw-r--r--src/tools/clippy/clippy_lints/src/borrow_deref_ref.rs113
-rw-r--r--src/tools/clippy/clippy_lints/src/box_default.rs32
-rw-r--r--src/tools/clippy/clippy_lints/src/cargo/common_metadata.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/cargo/feature_name.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/cargo/multiple_crate_versions.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/cargo/wildcard_dependencies.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/as_ptr_cast_mut.rs17
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/cast_abs_to_unsigned.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/cast_lossless.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/cast_ptr_alignment.rs22
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/cast_slice_different_sizes.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/cast_slice_from_raw_parts.rs14
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/mod.rs72
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/ptr_as_ptr.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/ptr_cast_constness.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs17
-rw-r--r--src/tools/clippy/clippy_lints/src/checked_conversions.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/cognitive_complexity.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/collapsible_if.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/collection_is_never_read.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/crate_in_macro_def.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/create_dir.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/declared_lints.rs19
-rw-r--r--src/tools/clippy/clippy_lints/src/default.rs18
-rw-r--r--src/tools/clippy/clippy_lints/src/default_constructed_unit_structs.rs9
-rw-r--r--src/tools/clippy/clippy_lints/src/default_instead_of_iter_empty.rs15
-rw-r--r--src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/default_union_representation.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/dereference.rs67
-rw-r--r--src/tools/clippy/clippy_lints/src/derivable_impls.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/derive.rs7
-rw-r--r--src/tools/clippy/clippy_lints/src/disallowed_macros.rs9
-rw-r--r--src/tools/clippy/clippy_lints/src/disallowed_methods.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/disallowed_names.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/disallowed_script_idents.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/disallowed_types.rs11
-rw-r--r--src/tools/clippy/clippy_lints/src/doc.rs50
-rw-r--r--src/tools/clippy/clippy_lints/src/double_parens.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/drop_forget_ref.rs14
-rw-r--r--src/tools/clippy/clippy_lints/src/else_if_without_else.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/empty_drop.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/empty_enum.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/empty_structs_with_brackets.rs12
-rw-r--r--src/tools/clippy/clippy_lints/src/entry.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/enum_clike.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/equatable_if_let.rs7
-rw-r--r--src/tools/clippy/clippy_lints/src/error_impl_error.rs30
-rw-r--r--src/tools/clippy/clippy_lints/src/escape.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/eta_reduction.rs66
-rw-r--r--src/tools/clippy/clippy_lints/src/excessive_bools.rs13
-rw-r--r--src/tools/clippy/clippy_lints/src/exhaustive_items.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/exit.rs7
-rw-r--r--src/tools/clippy/clippy_lints/src/explicit_write.rs47
-rw-r--r--src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs32
-rw-r--r--src/tools/clippy/clippy_lints/src/fallible_impl_from.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/float_literal.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs30
-rw-r--r--src/tools/clippy/clippy_lints/src/format.rs13
-rw-r--r--src/tools/clippy/clippy_lints/src/format_args.rs24
-rw-r--r--src/tools/clippy/clippy_lints/src/format_impl.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/format_push_string.rs16
-rw-r--r--src/tools/clippy/clippy_lints/src/formatting.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/four_forward_slashes.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/from_over_into.rs20
-rw-r--r--src/tools/clippy/clippy_lints/src/from_raw_with_void_ptr.rs30
-rw-r--r--src/tools/clippy/clippy_lints/src/functions/impl_trait_in_params.rs126
-rw-r--r--src/tools/clippy/clippy_lints/src/functions/mod.rs36
-rw-r--r--src/tools/clippy/clippy_lints/src/functions/must_use.rs7
-rw-r--r--src/tools/clippy/clippy_lints/src/functions/result.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/future_not_send.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/if_not_else.rs16
-rw-r--r--src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs21
-rw-r--r--src/tools/clippy/clippy_lints/src/ignored_unit_patterns.rs10
-rw-r--r--src/tools/clippy/clippy_lints/src/implicit_hasher.rs11
-rw-r--r--src/tools/clippy/clippy_lints/src/implicit_return.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/implicit_saturating_add.rs28
-rw-r--r--src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs82
-rw-r--r--src/tools/clippy/clippy_lints/src/inconsistent_struct_constructor.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/index_refutable_slice.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/indexing_slicing.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/infinite_iter.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/inherent_impl.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/inherent_to_string.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/init_numbered_fields.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/inline_fn_without_body.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/instant_subtraction.rs16
-rw-r--r--src/tools/clippy/clippy_lints/src/int_plus_one.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/invalid_upcast_comparisons.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/item_name_repetitions.rs (renamed from src/tools/clippy/clippy_lints/src/enum_variants.rs)228
-rw-r--r--src/tools/clippy/clippy_lints/src/items_after_statements.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/items_after_test_module.rs107
-rw-r--r--src/tools/clippy/clippy_lints/src/iter_not_returning_iterator.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/iter_without_into_iter.rs295
-rw-r--r--src/tools/clippy/clippy_lints/src/large_enum_variant.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/large_futures.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/large_stack_arrays.rs52
-rw-r--r--src/tools/clippy/clippy_lints/src/large_stack_frames.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/len_zero.rs38
-rw-r--r--src/tools/clippy/clippy_lints/src/let_underscore.rs29
-rw-r--r--src/tools/clippy/clippy_lints/src/let_with_type_underscore.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/lib.rs103
-rw-r--r--src/tools/clippy/clippy_lints/src/lifetimes.rs18
-rw-r--r--src/tools/clippy/clippy_lints/src/lines_filter_map_ok.rs55
-rw-r--r--src/tools/clippy/clippy_lints/src/literal_representation.rs12
-rw-r--r--src/tools/clippy/clippy_lints/src/loops/explicit_iter_loop.rs40
-rw-r--r--src/tools/clippy/clippy_lints/src/loops/for_kv_map.rs12
-rw-r--r--src/tools/clippy/clippy_lints/src/loops/manual_find.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/loops/manual_flatten.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/loops/mod.rs85
-rw-r--r--src/tools/clippy/clippy_lints/src/loops/mut_range_bound.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/loops/never_loop.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/loops/same_item_push.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/loops/unused_enumerate_index.rs62
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_assert.rs18
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_async_fn.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_bits.rs10
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_clamp.rs94
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_float_methods.rs92
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_hash_one.rs132
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs44
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_let_else.rs57
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_main_separator_str.rs48
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs30
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_range_patterns.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_rem_euclid.rs51
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_retain.rs52
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_slice_size_calculation.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_string_new.rs33
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_strip.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/map_unit_fn.rs11
-rw-r--r--src/tools/clippy/clippy_lints/src/matches/collapsible_match.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/matches/manual_filter.rs20
-rw-r--r--src/tools/clippy/clippy_lints/src/matches/manual_utils.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/matches/match_wild_err_arm.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/matches/mod.rs85
-rw-r--r--src/tools/clippy/clippy_lints/src/matches/overlapping_arms.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/matches/redundant_guards.rs39
-rw-r--r--src/tools/clippy/clippy_lints/src/matches/single_match.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/mem_replace.rs14
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/bytecount.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/bytes_nth.rs11
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/clone_on_ref_ptr.rs19
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/cloned_instead_of_copied.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/err_expect.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/filetype_is_file.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/filter_map.rs32
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/filter_map_bool_then.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/filter_map_identity.rs7
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/filter_map_next.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/flat_map_identity.rs7
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/flat_map_option.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/format_collect.rs16
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/from_iter_instead_of_collect.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/get_first.rs42
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/get_unwrap.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/inefficient_to_string.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/inspect_for_each.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/into_iter_on_ref.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/is_digit_ascii_radix.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/iter_kv_map.rs15
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/iter_out_of_bounds.rs12
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/iter_overeager_cloned.rs88
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/manual_try_fold.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/map_clone.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/map_identity.rs7
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/map_unwrap_or.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/mod.rs703
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/needless_collect.rs51
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/open_options.rs10
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/option_as_ref_deref.rs7
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/option_map_unwrap_or.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/path_ends_with_ext.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/read_line_without_trim.rs9
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/readonly_write_lock.rs21
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/search_is_some.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/seek_from_current.rs23
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/seek_to_start_instead_of_rewind.rs22
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/str_splitn.rs20
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/string_lit_chars_any.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/suspicious_command_arg_space.rs14
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/type_id_on_box.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/unnecessary_fallible_conversions.rs122
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/unnecessary_fold.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/unnecessary_lazy_eval.rs18
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/unnecessary_literal_unwrap.rs17
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs114
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/waker_clone_wake.rs32
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/wrong_self_convention.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/min_ident_chars.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/misc.rs10
-rw-r--r--src/tools/clippy/clippy_lints/src/misc_early/mod.rs34
-rw-r--r--src/tools/clippy/clippy_lints/src/misc_early/unneeded_wildcard_pattern.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/mismatching_type_param_order.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/missing_assert_message.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs27
-rw-r--r--src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/missing_doc.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/missing_enforced_import_rename.rs9
-rw-r--r--src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs16
-rw-r--r--src/tools/clippy/clippy_lints/src/missing_inline.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/missing_trait_methods.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/mixed_read_write_in_expression.rs24
-rw-r--r--src/tools/clippy/clippy_lints/src/multi_assignments.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/multiple_unsafe_ops_per_block.rs11
-rw-r--r--src/tools/clippy/clippy_lints/src/mut_key.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/mut_mut.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/mut_reference.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/mutex_atomic.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/needless_arbitrary_self_type.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/needless_bool.rs12
-rw-r--r--src/tools/clippy/clippy_lints/src/needless_borrowed_ref.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/needless_borrows_for_generic_args.rs43
-rw-r--r--src/tools/clippy/clippy_lints/src/needless_continue.rs14
-rw-r--r--src/tools/clippy/clippy_lints/src/needless_else.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/needless_for_each.rs7
-rw-r--r--src/tools/clippy/clippy_lints/src/needless_if.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/needless_late_init.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/needless_parens_on_range_literals.rs10
-rw-r--r--src/tools/clippy/clippy_lints/src/needless_pass_by_ref_mut.rs121
-rw-r--r--src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/needless_question_mark.rs10
-rw-r--r--src/tools/clippy/clippy_lints/src/needless_update.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/neg_cmp_op_on_partial_ord.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/neg_multiply.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/no_effect.rs13
-rw-r--r--src/tools/clippy/clippy_lints/src/no_mangle_with_rust_abi.rs7
-rw-r--r--src/tools/clippy/clippy_lints/src/non_canonical_impls.rs38
-rw-r--r--src/tools/clippy/clippy_lints/src/non_copy_const.rs323
-rw-r--r--src/tools/clippy/clippy_lints/src/non_expressive_names.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs14
-rw-r--r--src/tools/clippy/clippy_lints/src/non_send_fields_in_send_ty.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/nonstandard_macro_braces.rs89
-rw-r--r--src/tools/clippy/clippy_lints/src/octal_escapes.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/only_used_in_recursion.rs22
-rw-r--r--src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs45
-rw-r--r--src/tools/clippy/clippy_lints/src/operators/bit_mask.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/operators/cmp_owned.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/operators/const_comparisons.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/operators/double_comparison.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/operators/eq_op.rs19
-rw-r--r--src/tools/clippy/clippy_lints/src/operators/identity_op.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/operators/mod.rs76
-rw-r--r--src/tools/clippy/clippy_lints/src/operators/numeric_arithmetic.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/option_env_unwrap.rs23
-rw-r--r--src/tools/clippy/clippy_lints/src/option_if_let_else.rs14
-rw-r--r--src/tools/clippy/clippy_lints/src/overflow_check_conditional.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/panic_in_result_fn.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/panic_unimplemented.rs13
-rw-r--r--src/tools/clippy/clippy_lints/src/partial_pub_fields.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/partialeq_ne_impl.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/partialeq_to_none.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs13
-rw-r--r--src/tools/clippy/clippy_lints/src/pattern_type_mismatch.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/permissions_set_readonly_false.rs16
-rw-r--r--src/tools/clippy/clippy_lints/src/ptr.rs274
-rw-r--r--src/tools/clippy/clippy_lints/src/ptr_offset_with_cast.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/pub_use.rs27
-rw-r--r--src/tools/clippy/clippy_lints/src/question_mark.rs24
-rw-r--r--src/tools/clippy/clippy_lints/src/ranges.rs19
-rw-r--r--src/tools/clippy/clippy_lints/src/raw_strings.rs38
-rw-r--r--src/tools/clippy/clippy_lints/src/rc_clone_in_vec_init.rs13
-rw-r--r--src/tools/clippy/clippy_lints/src/read_zero_byte_vec.rs29
-rw-r--r--src/tools/clippy/clippy_lints/src/redundant_async_block.rs30
-rw-r--r--src/tools/clippy/clippy_lints/src/redundant_clone.rs9
-rw-r--r--src/tools/clippy/clippy_lints/src/redundant_closure_call.rs29
-rw-r--r--src/tools/clippy/clippy_lints/src/redundant_else.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/redundant_field_names.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/redundant_locals.rs42
-rw-r--r--src/tools/clippy/clippy_lints/src/redundant_pub_crate.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/redundant_slicing.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/redundant_static_lifetimes.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/redundant_type_annotations.rs24
-rw-r--r--src/tools/clippy/clippy_lints/src/ref_patterns.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/reference.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/regex.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/reserve_after_initialization.rs43
-rw-r--r--src/tools/clippy/clippy_lints/src/return_self_not_must_use.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/returns.rs15
-rw-r--r--src/tools/clippy/clippy_lints/src/same_name_method.rs16
-rw-r--r--src/tools/clippy/clippy_lints/src/semicolon_block.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/semicolon_if_nothing_returned.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/shadow.rs12
-rw-r--r--src/tools/clippy/clippy_lints/src/significant_drop_tightening.rs33
-rw-r--r--src/tools/clippy/clippy_lints/src/single_call_fn.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/single_char_lifetime_names.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/single_range_in_vec_init.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/size_of_in_element_count.rs22
-rw-r--r--src/tools/clippy/clippy_lints/src/size_of_ref.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/std_instead_of_core.rs39
-rw-r--r--src/tools/clippy/clippy_lints/src/strings.rs26
-rw-r--r--src/tools/clippy/clippy_lints/src/suspicious_doc_comments.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/suspicious_xor_used_as_pow.rs35
-rw-r--r--src/tools/clippy/clippy_lints/src/swap.rs10
-rw-r--r--src/tools/clippy/clippy_lints/src/swap_ptr_to_ref.rs24
-rw-r--r--src/tools/clippy/clippy_lints/src/tabs_in_doc_comments.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/temporary_assignment.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/tests_outside_test_module.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/to_digit_is_some.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/trailing_empty_array.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/trait_bounds.rs18
-rw-r--r--src/tools/clippy/clippy_lints/src/transmute/mod.rs38
-rw-r--r--src/tools/clippy/clippy_lints/src/transmute/transmute_ptr_to_ref.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/transmute/transmute_undefined_repr.rs22
-rw-r--r--src/tools/clippy/clippy_lints/src/transmute/transmuting_null.rs12
-rw-r--r--src/tools/clippy/clippy_lints/src/tuple_array_conversions.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/types/mod.rs20
-rw-r--r--src/tools/clippy/clippy_lints/src/types/utils.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs24
-rw-r--r--src/tools/clippy/clippy_lints/src/unicode.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/uninit_vec.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/unit_return_expecting_ord.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/unit_types/let_unit_value.rs11
-rw-r--r--src/tools/clippy/clippy_lints/src/unit_types/mod.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/unnamed_address.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/unnecessary_box_returns.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/unnecessary_map_on_constructor.rs38
-rw-r--r--src/tools/clippy/clippy_lints/src/unnecessary_owned_empty_strings.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/unnecessary_self_imports.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/unnecessary_struct_initialization.rs20
-rw-r--r--src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/unsafe_removed_from_name.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/unused_async.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/unused_peekable.rs24
-rw-r--r--src/tools/clippy/clippy_lints/src/unused_rounding.rs23
-rw-r--r--src/tools/clippy/clippy_lints/src/unused_unit.rs7
-rw-r--r--src/tools/clippy/clippy_lints/src/unwrap.rs9
-rw-r--r--src/tools/clippy/clippy_lints/src/unwrap_in_result.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/upper_case_acronyms.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/use_self.rs9
-rw-r--r--src/tools/clippy/clippy_lints/src/useless_conversion.rs73
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/author.rs39
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/dump_hir.rs12
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/format_args_collector.rs15
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/internal_lints.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/internal_lints/almost_standard_lint_formulation.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/internal_lints/if_chain_style.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs23
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs30
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/internal_lints/unsorted_clippy_utils_paths.rs (renamed from src/tools/clippy/clippy_lints/src/utils/internal_lints/clippy_lints_internal.rs)10
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/mod.rs143
-rw-r--r--src/tools/clippy/clippy_lints/src/vec.rs14
-rw-r--r--src/tools/clippy/clippy_lints/src/vec_init_then_push.rs10
-rw-r--r--src/tools/clippy/clippy_lints/src/visibility.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/wildcard_imports.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/write.rs181
-rw-r--r--src/tools/clippy/clippy_lints/src/zero_div_zero.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/zero_sized_map_values.rs4
-rw-r--r--src/tools/clippy/clippy_utils/Cargo.toml6
-rw-r--r--src/tools/clippy/clippy_utils/src/ast_utils.rs2
-rw-r--r--src/tools/clippy/clippy_utils/src/check_proc_macro.rs78
-rw-r--r--src/tools/clippy/clippy_utils/src/consts.rs95
-rw-r--r--src/tools/clippy/clippy_utils/src/diagnostics.rs4
-rw-r--r--src/tools/clippy/clippy_utils/src/higher.rs4
-rw-r--r--src/tools/clippy/clippy_utils/src/hir_utils.rs8
-rw-r--r--src/tools/clippy/clippy_utils/src/lib.rs263
-rw-r--r--src/tools/clippy/clippy_utils/src/macros.rs18
-rw-r--r--src/tools/clippy/clippy_utils/src/paths.rs70
-rw-r--r--src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs29
-rw-r--r--src/tools/clippy/clippy_utils/src/source.rs8
-rw-r--r--src/tools/clippy/clippy_utils/src/str_utils.rs67
-rw-r--r--src/tools/clippy/clippy_utils/src/sugg.rs9
-rw-r--r--src/tools/clippy/clippy_utils/src/ty.rs44
-rw-r--r--src/tools/clippy/clippy_utils/src/ty/type_certainty/mod.rs4
-rw-r--r--src/tools/clippy/clippy_utils/src/visitors.rs44
-rw-r--r--src/tools/clippy/declare_clippy_lint/Cargo.toml2
-rw-r--r--src/tools/clippy/declare_clippy_lint/src/lib.rs28
-rw-r--r--src/tools/clippy/lintcheck/src/main.rs2
-rw-r--r--src/tools/clippy/rust-toolchain2
-rw-r--r--src/tools/clippy/src/driver.rs73
-rw-r--r--src/tools/clippy/src/main.rs63
-rw-r--r--src/tools/clippy/tests/compile-test.rs89
-rw-r--r--src/tools/clippy/tests/dogfood.rs12
-rw-r--r--src/tools/clippy/tests/ui-internal/invalid_msrv_attr_impl.fixed2
-rw-r--r--src/tools/clippy/tests/ui-internal/invalid_msrv_attr_impl.rs2
-rw-r--r--src/tools/clippy/tests/ui-internal/unnecessary_def_path_hardcoded_path.rs4
-rw-r--r--src/tools/clippy/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr4
-rw-r--r--src/tools/clippy/tests/ui-toml/borrow_interior_mutable_const/clippy.toml1
-rw-r--r--src/tools/clippy/tests/ui-toml/borrow_interior_mutable_const/ignore.rs37
-rw-r--r--src/tools/clippy/tests/ui-toml/declare_interior_mutable_const/clippy.toml1
-rw-r--r--src/tools/clippy/tests/ui-toml/declare_interior_mutable_const/ignore.rs46
-rw-r--r--src/tools/clippy/tests/ui-toml/enum_variant_names/enum_variant_names.rs16
-rw-r--r--src/tools/clippy/tests/ui-toml/enum_variant_names/enum_variant_names.stderr18
-rw-r--r--src/tools/clippy/tests/ui-toml/impl_trait_in_params/clippy.toml1
-rw-r--r--src/tools/clippy/tests/ui-toml/impl_trait_in_params/impl_trait_in_params.rs16
-rw-r--r--src/tools/clippy/tests/ui-toml/impl_trait_in_params/impl_trait_in_params.stderr15
-rw-r--r--src/tools/clippy/tests/ui-toml/invalid_min_rust_version/invalid_min_rust_version.rs2
-rw-r--r--src/tools/clippy/tests/ui-toml/invalid_min_rust_version/invalid_min_rust_version.stderr6
-rw-r--r--src/tools/clippy/tests/ui-toml/item_name_repetitions/threshold0/clippy.toml (renamed from src/tools/clippy/tests/ui-toml/enum_variants_threshold0/clippy.toml)1
-rw-r--r--src/tools/clippy/tests/ui-toml/item_name_repetitions/threshold0/item_name_repetitions.rs (renamed from src/tools/clippy/tests/ui-toml/enum_variants_threshold0/enum_variants_name_threshold.rs)2
-rw-r--r--src/tools/clippy/tests/ui-toml/item_name_repetitions/threshold5/clippy.toml (renamed from src/tools/clippy/tests/ui-toml/enum_variant_names/clippy.toml)1
-rw-r--r--src/tools/clippy/tests/ui-toml/item_name_repetitions/threshold5/item_name_repetitions.rs32
-rw-r--r--src/tools/clippy/tests/ui-toml/item_name_repetitions/threshold5/item_name_repetitions.stderr34
-rw-r--r--src/tools/clippy/tests/ui-toml/pub_crate_missing_docs/pub_crate_missing_doc.rs1
-rw-r--r--src/tools/clippy/tests/ui-toml/pub_crate_missing_docs/pub_crate_missing_doc.stderr14
-rw-r--r--src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr2
-rw-r--r--src/tools/clippy/tests/ui-toml/too_many_arguments/too_many_arguments.stderr2
-rw-r--r--src/tools/clippy/tests/ui/author/blocks.stdout4
-rw-r--r--src/tools/clippy/tests/ui/author/macro_in_closure.rs5
-rw-r--r--src/tools/clippy/tests/ui/author/macro_in_closure.stdout39
-rw-r--r--src/tools/clippy/tests/ui/author/macro_in_loop.rs8
-rw-r--r--src/tools/clippy/tests/ui/author/macro_in_loop.stdout48
-rw-r--r--src/tools/clippy/tests/ui/auxiliary/proc_macro_derive.rs28
-rw-r--r--src/tools/clippy/tests/ui/auxiliary/proc_macros.rs10
-rw-r--r--src/tools/clippy/tests/ui/bool_to_int_with_if.fixed8
-rw-r--r--src/tools/clippy/tests/ui/bool_to_int_with_if.rs8
-rw-r--r--src/tools/clippy/tests/ui/bool_to_int_with_if.stderr2
-rw-r--r--src/tools/clippy/tests/ui/builtin_type_shadow.stderr2
-rw-r--r--src/tools/clippy/tests/ui/comparison_to_empty.fixed8
-rw-r--r--src/tools/clippy/tests/ui/comparison_to_empty.rs8
-rw-r--r--src/tools/clippy/tests/ui/comparison_to_empty.stderr8
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-10645.stderr4
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-11230.rs6
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-11755.rs5
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-5238.rs2
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-6252.stderr2
-rw-r--r--src/tools/clippy/tests/ui/doc/doc-fixable.fixed3
-rw-r--r--src/tools/clippy/tests/ui/doc/doc-fixable.rs3
-rw-r--r--src/tools/clippy/tests/ui/doc_unsafe.rs2
-rw-r--r--src/tools/clippy/tests/ui/enum_glob_use.fixed1
-rw-r--r--src/tools/clippy/tests/ui/enum_glob_use.rs1
-rw-r--r--src/tools/clippy/tests/ui/enum_variants.rs17
-rw-r--r--src/tools/clippy/tests/ui/enum_variants.stderr14
-rw-r--r--src/tools/clippy/tests/ui/floating_point_mul_add.fixed3
-rw-r--r--src/tools/clippy/tests/ui/floating_point_mul_add.rs3
-rw-r--r--src/tools/clippy/tests/ui/floating_point_mul_add.stderr8
-rw-r--r--src/tools/clippy/tests/ui/functions.stderr6
-rw-r--r--src/tools/clippy/tests/ui/future_not_send.stderr37
-rw-r--r--src/tools/clippy/tests/ui/get_first.fixed18
-rw-r--r--src/tools/clippy/tests/ui/get_first.rs18
-rw-r--r--src/tools/clippy/tests/ui/get_first.stderr24
-rw-r--r--src/tools/clippy/tests/ui/if_not_else_bittest.rs11
-rw-r--r--src/tools/clippy/tests/ui/ignored_unit_patterns.fixed31
-rw-r--r--src/tools/clippy/tests/ui/ignored_unit_patterns.rs31
-rw-r--r--src/tools/clippy/tests/ui/ignored_unit_patterns.stderr36
-rw-r--r--src/tools/clippy/tests/ui/impl_trait_in_params.rs35
-rw-r--r--src/tools/clippy/tests/ui/impl_trait_in_params.stderr30
-rw-r--r--src/tools/clippy/tests/ui/implied_bounds_in_impls.fixed1
-rw-r--r--src/tools/clippy/tests/ui/implied_bounds_in_impls.rs1
-rw-r--r--src/tools/clippy/tests/ui/implied_bounds_in_impls.stderr32
-rw-r--r--src/tools/clippy/tests/ui/into_iter_without_iter.rs148
-rw-r--r--src/tools/clippy/tests/ui/into_iter_without_iter.stderr114
-rw-r--r--src/tools/clippy/tests/ui/items_after_test_module/after_proc_macros.rs11
-rw-r--r--src/tools/clippy/tests/ui/items_after_test_module/auxiliary/submodule.rs4
-rw-r--r--src/tools/clippy/tests/ui/items_after_test_module/block_module.stderr2
-rw-r--r--src/tools/clippy/tests/ui/items_after_test_module/in_submodule.rs8
-rw-r--r--src/tools/clippy/tests/ui/items_after_test_module/in_submodule.stderr14
-rw-r--r--src/tools/clippy/tests/ui/items_after_test_module/multiple_modules.rs11
-rw-r--r--src/tools/clippy/tests/ui/items_after_test_module/root_module.fixed (renamed from src/tools/clippy/tests/ui/items_after_test_module/block_module.rs)15
-rw-r--r--src/tools/clippy/tests/ui/items_after_test_module/root_module.rs22
-rw-r--r--src/tools/clippy/tests/ui/items_after_test_module/root_module.stderr20
-rw-r--r--src/tools/clippy/tests/ui/iter_without_into_iter.rs124
-rw-r--r--src/tools/clippy/tests/ui/iter_without_into_iter.stderr150
-rw-r--r--src/tools/clippy/tests/ui/large_futures.fixed2
-rw-r--r--src/tools/clippy/tests/ui/large_futures.rs2
-rw-r--r--src/tools/clippy/tests/ui/let_and_return.fixed21
-rw-r--r--src/tools/clippy/tests/ui/let_and_return.rs19
-rw-r--r--src/tools/clippy/tests/ui/let_and_return.stderr26
-rw-r--r--src/tools/clippy/tests/ui/manual_filter.rs4
-rw-r--r--src/tools/clippy/tests/ui/manual_filter.stderr6
-rw-r--r--src/tools/clippy/tests/ui/manual_filter_map.fixed1
-rw-r--r--src/tools/clippy/tests/ui/manual_filter_map.rs1
-rw-r--r--src/tools/clippy/tests/ui/manual_filter_map.stderr76
-rw-r--r--src/tools/clippy/tests/ui/manual_find_map.fixed1
-rw-r--r--src/tools/clippy/tests/ui/manual_find_map.rs1
-rw-r--r--src/tools/clippy/tests/ui/manual_find_map.stderr78
-rw-r--r--src/tools/clippy/tests/ui/manual_hash_one.fixed89
-rw-r--r--src/tools/clippy/tests/ui/manual_hash_one.rs89
-rw-r--r--src/tools/clippy/tests/ui/manual_hash_one.stderr56
-rw-r--r--src/tools/clippy/tests/ui/manual_is_ascii_check.fixed4
-rw-r--r--src/tools/clippy/tests/ui/manual_is_ascii_check.rs4
-rw-r--r--src/tools/clippy/tests/ui/manual_is_ascii_check.stderr22
-rw-r--r--src/tools/clippy/tests/ui/manual_let_else.rs4
-rw-r--r--src/tools/clippy/tests/ui/manual_let_else.stderr48
-rw-r--r--src/tools/clippy/tests/ui/manual_let_else_match.fixed4
-rw-r--r--src/tools/clippy/tests/ui/manual_let_else_match.rs8
-rw-r--r--src/tools/clippy/tests/ui/manual_let_else_match.stderr12
-rw-r--r--src/tools/clippy/tests/ui/manual_map_option_2.fixed4
-rw-r--r--src/tools/clippy/tests/ui/manual_map_option_2.rs4
-rw-r--r--src/tools/clippy/tests/ui/manual_map_option_2.stderr19
-rw-r--r--src/tools/clippy/tests/ui/manual_non_exhaustive_enum.rs3
-rw-r--r--src/tools/clippy/tests/ui/manual_non_exhaustive_enum.stderr20
-rw-r--r--src/tools/clippy/tests/ui/manual_string_new.fixed1
-rw-r--r--src/tools/clippy/tests/ui/manual_string_new.rs1
-rw-r--r--src/tools/clippy/tests/ui/manual_string_new.stderr18
-rw-r--r--src/tools/clippy/tests/ui/map_identity.fixed27
-rw-r--r--src/tools/clippy/tests/ui/map_identity.rs29
-rw-r--r--src/tools/clippy/tests/ui/map_identity.stderr29
-rw-r--r--src/tools/clippy/tests/ui/match_same_arms_non_exhaustive.rs22
-rw-r--r--src/tools/clippy/tests/ui/match_same_arms_non_exhaustive.stderr16
-rw-r--r--src/tools/clippy/tests/ui/min_ident_chars.rs1
-rw-r--r--src/tools/clippy/tests/ui/min_ident_chars.stderr58
-rw-r--r--src/tools/clippy/tests/ui/min_rust_version_invalid_attr.rs5
-rw-r--r--src/tools/clippy/tests/ui/min_rust_version_invalid_attr.stderr24
-rw-r--r--src/tools/clippy/tests/ui/misnamed_getters.fixed1
-rw-r--r--src/tools/clippy/tests/ui/misnamed_getters.rs1
-rw-r--r--src/tools/clippy/tests/ui/misnamed_getters.stderr36
-rw-r--r--src/tools/clippy/tests/ui/missing_const_for_fn/auxiliary/helper.rs4
-rw-r--r--src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.rs7
-rw-r--r--src/tools/clippy/tests/ui/must_use_unit.stderr4
-rw-r--r--src/tools/clippy/tests/ui/needless_bool/fixable.fixed3
-rw-r--r--src/tools/clippy/tests/ui/needless_bool/fixable.rs3
-rw-r--r--src/tools/clippy/tests/ui/needless_bool/fixable.stderr42
-rw-r--r--src/tools/clippy/tests/ui/needless_if.fixed16
-rw-r--r--src/tools/clippy/tests/ui/needless_if.rs16
-rw-r--r--src/tools/clippy/tests/ui/needless_if.stderr21
-rw-r--r--src/tools/clippy/tests/ui/needless_late_init.fixed4
-rw-r--r--src/tools/clippy/tests/ui/needless_late_init.rs4
-rw-r--r--src/tools/clippy/tests/ui/needless_pass_by_ref_mut.rs45
-rw-r--r--src/tools/clippy/tests/ui/needless_pass_by_ref_mut.stderr62
-rw-r--r--src/tools/clippy/tests/ui/needless_raw_string.fixed4
-rw-r--r--src/tools/clippy/tests/ui/needless_raw_string.rs4
-rw-r--r--src/tools/clippy/tests/ui/needless_raw_string.stderr46
-rw-r--r--src/tools/clippy/tests/ui/needless_raw_string_hashes.stderr30
-rw-r--r--src/tools/clippy/tests/ui/option_if_let_else.fixed17
-rw-r--r--src/tools/clippy/tests/ui/option_if_let_else.rs19
-rw-r--r--src/tools/clippy/tests/ui/option_if_let_else.stderr67
-rw-r--r--src/tools/clippy/tests/ui/print_literal.fixed18
-rw-r--r--src/tools/clippy/tests/ui/print_literal.rs18
-rw-r--r--src/tools/clippy/tests/ui/print_literal.stderr124
-rw-r--r--src/tools/clippy/tests/ui/redundant_guards.fixed2
-rw-r--r--src/tools/clippy/tests/ui/redundant_guards.rs2
-rw-r--r--src/tools/clippy/tests/ui/redundant_locals.rs45
-rw-r--r--src/tools/clippy/tests/ui/redundant_locals.stderr139
-rw-r--r--src/tools/clippy/tests/ui/redundant_pattern_matching_option.fixed13
-rw-r--r--src/tools/clippy/tests/ui/redundant_pattern_matching_option.rs13
-rw-r--r--src/tools/clippy/tests/ui/redundant_pattern_matching_option.stderr56
-rw-r--r--src/tools/clippy/tests/ui/rest_pat_in_fully_bound_structs.rs1
-rw-r--r--src/tools/clippy/tests/ui/rest_pat_in_fully_bound_structs.stderr6
-rw-r--r--src/tools/clippy/tests/ui/std_instead_of_core.fixed11
-rw-r--r--src/tools/clippy/tests/ui/std_instead_of_core.rs11
-rw-r--r--src/tools/clippy/tests/ui/std_instead_of_core.stderr22
-rw-r--r--src/tools/clippy/tests/ui/struct_fields.rs331
-rw-r--r--src/tools/clippy/tests/ui/struct_fields.stderr265
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_fallible_conversions.fixed6
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_fallible_conversions.rs6
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_fallible_conversions.stderr17
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_fallible_conversions_unfixable.rs43
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_fallible_conversions_unfixable.stderr41
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_lazy_eval.fixed3
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_lazy_eval.rs3
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_lazy_eval.stderr94
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_lazy_eval_unfixable.rs5
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_lazy_eval_unfixable.stderr10
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_literal_unwrap.fixed4
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_literal_unwrap.stderr4
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_to_owned.fixed23
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_to_owned.rs23
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_to_owned.stderr176
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_unsafety_doc.rs2
-rw-r--r--src/tools/clippy/tests/ui/unused_async.rs1
-rw-r--r--src/tools/clippy/tests/ui/unused_async.stderr10
-rw-r--r--src/tools/clippy/tests/ui/unused_enumerate_index.fixed58
-rw-r--r--src/tools/clippy/tests/ui/unused_enumerate_index.rs58
-rw-r--r--src/tools/clippy/tests/ui/unused_enumerate_index.stderr26
-rw-r--r--src/tools/clippy/tests/ui/useless_conversion_try.rs2
-rw-r--r--src/tools/clippy/tests/ui/waker_clone_wake.fixed29
-rw-r--r--src/tools/clippy/tests/ui/waker_clone_wake.rs29
-rw-r--r--src/tools/clippy/tests/ui/waker_clone_wake.stderr17
-rw-r--r--src/tools/clippy/tests/ui/wildcard_imports.fixed28
-rw-r--r--src/tools/clippy/tests/ui/wildcard_imports.rs28
-rw-r--r--src/tools/clippy/tests/ui/wildcard_imports.stderr38
-rw-r--r--src/tools/clippy/tests/ui/wildcard_imports_2021.edition2018.fixed28
-rw-r--r--src/tools/clippy/tests/ui/wildcard_imports_2021.edition2018.stderr38
-rw-r--r--src/tools/clippy/tests/ui/wildcard_imports_2021.edition2021.fixed28
-rw-r--r--src/tools/clippy/tests/ui/wildcard_imports_2021.edition2021.stderr38
-rw-r--r--src/tools/clippy/tests/ui/wildcard_imports_2021.rs28
-rw-r--r--src/tools/clippy/tests/ui/write_literal.fixed14
-rw-r--r--src/tools/clippy/tests/ui/write_literal.rs14
-rw-r--r--src/tools/clippy/tests/ui/write_literal.stderr74
-rw-r--r--src/tools/clippy/tests/ui/write_literal_2.rs16
-rw-r--r--src/tools/clippy/tests/ui/write_literal_2.stderr82
-rw-r--r--src/tools/clippy/tests/versioncheck.rs1
-rw-r--r--src/tools/clippy/triagebot.toml2
-rw-r--r--src/tools/compiletest/Cargo.toml1
-rw-r--r--src/tools/compiletest/src/common.rs23
-rw-r--r--src/tools/compiletest/src/header.rs14
-rw-r--r--src/tools/compiletest/src/header/needs.rs4
-rw-r--r--src/tools/compiletest/src/header/tests.rs2
-rw-r--r--src/tools/compiletest/src/lib.rs12
-rw-r--r--src/tools/compiletest/src/runtest.rs145
-rw-r--r--src/tools/lld-wrapper/src/main.rs4
-rw-r--r--src/tools/miropt-test-tools/src/lib.rs27
-rw-r--r--src/tools/opt-dist/Cargo.toml1
-rw-r--r--src/tools/opt-dist/src/bolt.rs14
-rw-r--r--src/tools/opt-dist/src/exec.rs9
-rw-r--r--src/tools/opt-dist/src/main.rs76
-rw-r--r--src/tools/opt-dist/src/tests.rs4
-rw-r--r--src/tools/opt-dist/src/training.rs53
-rw-r--r--src/tools/opt-dist/src/utils/artifact_size.rs61
-rw-r--r--src/tools/opt-dist/src/utils/mod.rs51
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/attr/builtin.rs18
-rw-r--r--src/tools/rust-analyzer/crates/parser/src/syntax_kind.rs2
-rw-r--r--src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs1
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/tests/sourcegen_ast.rs1
-rw-r--r--src/tools/rust-installer/Cargo.toml1
-rw-r--r--src/tools/rustdoc-gui/tester.js9
-rw-r--r--src/tools/rustfmt/.github/workflows/check_diff.yml2
-rw-r--r--src/tools/rustfmt/CHANGELOG.md80
-rw-r--r--src/tools/rustfmt/CODE_OF_CONDUCT.md2
-rw-r--r--src/tools/rustfmt/Cargo.lock445
-rw-r--r--src/tools/rustfmt/Cargo.toml17
-rw-r--r--src/tools/rustfmt/Contributing.md12
-rw-r--r--src/tools/rustfmt/README.md2
-rwxr-xr-xsrc/tools/rustfmt/ci/build_and_test.bat6
-rwxr-xr-xsrc/tools/rustfmt/ci/build_and_test.sh6
-rwxr-xr-xsrc/tools/rustfmt/ci/check_diff.sh13
-rw-r--r--src/tools/rustfmt/config_proc_macro/Cargo.toml2
-rw-r--r--src/tools/rustfmt/rust-toolchain2
-rw-r--r--src/tools/rustfmt/src/attr.rs8
-rw-r--r--src/tools/rustfmt/src/attr/doc_comment.rs6
-rw-r--r--src/tools/rustfmt/src/bin/main.rs26
-rw-r--r--src/tools/rustfmt/src/cargo-fmt/main.rs30
-rw-r--r--src/tools/rustfmt/src/chains.rs91
-rw-r--r--src/tools/rustfmt/src/closures.rs15
-rw-r--r--src/tools/rustfmt/src/comment.rs74
-rw-r--r--src/tools/rustfmt/src/config/config_type.rs10
-rw-r--r--src/tools/rustfmt/src/config/file_lines.rs2
-rw-r--r--src/tools/rustfmt/src/config/macro_names.rs16
-rw-r--r--src/tools/rustfmt/src/config/mod.rs15
-rw-r--r--src/tools/rustfmt/src/config/options.rs4
-rw-r--r--src/tools/rustfmt/src/emitter.rs2
-rw-r--r--src/tools/rustfmt/src/emitter/checkstyle.rs4
-rw-r--r--src/tools/rustfmt/src/emitter/checkstyle/xml.rs2
-rw-r--r--src/tools/rustfmt/src/emitter/diff.rs6
-rw-r--r--src/tools/rustfmt/src/emitter/json.rs6
-rw-r--r--src/tools/rustfmt/src/emitter/stdout.rs4
-rw-r--r--src/tools/rustfmt/src/expr.rs139
-rw-r--r--src/tools/rustfmt/src/format-diff/main.rs15
-rw-r--r--src/tools/rustfmt/src/formatting.rs2
-rw-r--r--src/tools/rustfmt/src/git-rustfmt/main.rs11
-rw-r--r--src/tools/rustfmt/src/imports.rs26
-rw-r--r--src/tools/rustfmt/src/items.rs380
-rw-r--r--src/tools/rustfmt/src/lib.rs11
-rw-r--r--src/tools/rustfmt/src/lists.rs2
-rw-r--r--src/tools/rustfmt/src/macros.rs84
-rw-r--r--src/tools/rustfmt/src/matches.rs25
-rw-r--r--src/tools/rustfmt/src/pairs.rs45
-rw-r--r--src/tools/rustfmt/src/parse/session.rs3
-rw-r--r--src/tools/rustfmt/src/patterns.rs8
-rw-r--r--src/tools/rustfmt/src/rustfmt_diff.rs17
-rw-r--r--src/tools/rustfmt/src/skip.rs2
-rw-r--r--src/tools/rustfmt/src/source_file.rs2
-rw-r--r--src/tools/rustfmt/src/stmt.rs34
-rw-r--r--src/tools/rustfmt/src/string.rs4
-rw-r--r--src/tools/rustfmt/src/test/configuration_snippet.rs10
-rw-r--r--src/tools/rustfmt/src/test/mod.rs24
-rw-r--r--src/tools/rustfmt/src/types.rs46
-rw-r--r--src/tools/rustfmt/src/utils.rs43
-rw-r--r--src/tools/rustfmt/tests/cargo-fmt/main.rs2
-rw-r--r--src/tools/rustfmt/tests/config/issue-5816.toml1
-rw-r--r--src/tools/rustfmt/tests/rustfmt/main.rs6
-rw-r--r--src/tools/rustfmt/tests/source/immovable_coroutines.rs (renamed from src/tools/rustfmt/tests/source/immovable_generators.rs)2
-rw-r--r--src/tools/rustfmt/tests/source/issue-3984.rs12
-rw-r--r--src/tools/rustfmt/tests/source/issue-4808.rs13
-rw-r--r--src/tools/rustfmt/tests/source/issue-5655/one.rs9
-rw-r--r--src/tools/rustfmt/tests/source/issue-5655/two.rs9
-rw-r--r--src/tools/rustfmt/tests/source/issue-5791.rs3
-rw-r--r--src/tools/rustfmt/tests/source/issue-5835.rs8
-rw-r--r--src/tools/rustfmt/tests/source/issue-5852/default.rs104
-rw-r--r--src/tools/rustfmt/tests/source/issue-5852/horizontal.rs106
-rw-r--r--src/tools/rustfmt/tests/source/issue-5852/horizontal_vertical.rs106
-rw-r--r--src/tools/rustfmt/tests/source/issue-5852/issue_example.rs8
-rw-r--r--src/tools/rustfmt/tests/source/issue-5852/split.rs111
-rw-r--r--src/tools/rustfmt/tests/source/issue-5852/vertical.rs106
-rw-r--r--src/tools/rustfmt/tests/source/issue-5935.rs9
-rw-r--r--src/tools/rustfmt/tests/source/issue_5721.rs8
-rw-r--r--src/tools/rustfmt/tests/source/issue_5730.rs3
-rw-r--r--src/tools/rustfmt/tests/source/issue_5735.rs6
-rw-r--r--src/tools/rustfmt/tests/source/issue_5882.rs7
-rw-r--r--src/tools/rustfmt/tests/source/let_chains.rs121
-rw-r--r--src/tools/rustfmt/tests/source/let_else.rs20
-rw-r--r--src/tools/rustfmt/tests/source/let_else_v2.rs56
-rw-r--r--src/tools/rustfmt/tests/source/match.rs3
-rw-r--r--src/tools/rustfmt/tests/source/non-lifetime-binders.rs10
-rw-r--r--src/tools/rustfmt/tests/source/skip_macro_invocations/config_file.rs9
-rw-r--r--src/tools/rustfmt/tests/target/extern-rust.rs1
-rw-r--r--src/tools/rustfmt/tests/target/immovable_coroutines.rs (renamed from src/tools/rustfmt/tests/target/immovable_generators.rs)2
-rw-r--r--src/tools/rustfmt/tests/target/issue-3984.rs12
-rw-r--r--src/tools/rustfmt/tests/target/issue-4808.rs13
-rw-r--r--src/tools/rustfmt/tests/target/issue-5568.rs14
-rw-r--r--src/tools/rustfmt/tests/target/issue-5655/one.rs9
-rw-r--r--src/tools/rustfmt/tests/target/issue-5655/two.rs8
-rw-r--r--src/tools/rustfmt/tests/target/issue-5791.rs3
-rw-r--r--src/tools/rustfmt/tests/target/issue-5797/retain_trailing_semicolon.rs7
-rw-r--r--src/tools/rustfmt/tests/target/issue-5835.rs8
-rw-r--r--src/tools/rustfmt/tests/target/issue-5852/default.rs97
-rw-r--r--src/tools/rustfmt/tests/target/issue-5852/horizontal.rs99
-rw-r--r--src/tools/rustfmt/tests/target/issue-5852/horizontal_vertical.rs99
-rw-r--r--src/tools/rustfmt/tests/target/issue-5852/issue_example.rs8
-rw-r--r--src/tools/rustfmt/tests/target/issue-5852/split.rs102
-rw-r--r--src/tools/rustfmt/tests/target/issue-5852/vertical.rs105
-rw-r--r--src/tools/rustfmt/tests/target/issue-5871.rs8
-rw-r--r--src/tools/rustfmt/tests/target/issue-5935.rs8
-rw-r--r--src/tools/rustfmt/tests/target/issue_4110.rs2
-rw-r--r--src/tools/rustfmt/tests/target/issue_5533.rs6
-rw-r--r--src/tools/rustfmt/tests/target/issue_5542.rs10
-rw-r--r--src/tools/rustfmt/tests/target/issue_5676.rs8
-rw-r--r--src/tools/rustfmt/tests/target/issue_5721.rs10
-rw-r--r--src/tools/rustfmt/tests/target/issue_5730.rs3
-rw-r--r--src/tools/rustfmt/tests/target/issue_5735.rs6
-rw-r--r--src/tools/rustfmt/tests/target/issue_5882.rs7
-rw-r--r--src/tools/rustfmt/tests/target/issue_5907.rs6
-rw-r--r--src/tools/rustfmt/tests/target/let_chains.rs129
-rw-r--r--src/tools/rustfmt/tests/target/let_else.rs31
-rw-r--r--src/tools/rustfmt/tests/target/let_else_v2.rs73
-rw-r--r--src/tools/rustfmt/tests/target/match.rs8
-rw-r--r--src/tools/rustfmt/tests/target/non-lifetime-binders.rs10
-rw-r--r--src/tools/rustfmt/tests/target/skip_macro_invocations/config_file.rs7
-rw-r--r--src/tools/rustfmt/triagebot.toml3
-rw-r--r--src/tools/suggest-tests/src/main.rs21
-rw-r--r--src/tools/tidy/src/alphabetical.rs94
-rw-r--r--src/tools/tidy/src/alphabetical/tests.rs188
-rw-r--r--src/tools/tidy/src/deps.rs268
-rw-r--r--src/tools/tidy/src/extdeps.rs36
-rw-r--r--src/tools/tidy/src/features.rs16
-rw-r--r--src/tools/tidy/src/lib.rs16
-rw-r--r--src/tools/tidy/src/mir_opt_tests.rs3
-rw-r--r--src/tools/tidy/src/style.rs7
-rw-r--r--src/tools/tidy/src/ui_tests.rs2
-rw-r--r--src/version2
1317 files changed, 30078 insertions, 14765 deletions
diff --git a/src/bootstrap/CHANGELOG.md b/src/bootstrap/CHANGELOG.md
deleted file mode 100644
index 1aba07138..000000000
--- a/src/bootstrap/CHANGELOG.md
+++ /dev/null
@@ -1,71 +0,0 @@
-# Changelog
-
-All notable changes to bootstrap will be documented in this file.
-
-The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
-
-
-## [Changes since the last major version]
-
-- Vendoring is no longer done automatically when building from git sources. To use vendoring, run `cargo vendor` manually, or use the pre-vendored `rustc-src` tarball.
-- `llvm-libunwind` now accepts `in-tree` (formerly true), `system` or `no` (formerly false) [#77703](https://github.com/rust-lang/rust/pull/77703)
-- The options `infodir`, `localstatedir`, and `gpg-password-file` are no longer allowed in config.toml. Previously, they were ignored without warning. Note that `infodir` and `localstatedir` are still accepted by `./configure`, with a warning. [#82451](https://github.com/rust-lang/rust/pull/82451)
-- Change the names for `dist` commands to match the component they generate. [#90684](https://github.com/rust-lang/rust/pull/90684)
-- The `build.fast-submodules` option has been removed. Fast submodule checkouts are enabled unconditionally. Automatic submodule handling can still be disabled with `build.submodules = false`.
-- Several unsupported `./configure` options have been removed: `optimize`, `parallel-compiler`. These can still be enabled with `--set`, although it isn't recommended.
-- `remote-test-server`'s `verbose` argument has been removed in favor of the `--verbose` flag
-- `remote-test-server`'s `remote` argument has been removed in favor of the `--bind` flag. Use `--bind 0.0.0.0:12345` to replicate the behavior of the `remote` argument.
-- `x.py fmt` now formats only files modified between the merge-base of HEAD and the last commit in the master branch of the rust-lang repository and the current working directory. To restore old behaviour, use `x.py fmt .`. The check mode is not affected by this change. [#105702](https://github.com/rust-lang/rust/pull/105702)
-- The `llvm.version-check` config option has been removed. Older versions were never supported. If you still need to support older versions (e.g. you are applying custom patches), patch `check_llvm_version` in bootstrap to change the minimum version. [#108619](https://github.com/rust-lang/rust/pull/108619)
-- The `rust.ignore-git` option has been renamed to `rust.omit-git-hash`. [#110059](https://github.com/rust-lang/rust/pull/110059)
-- `--exclude` no longer accepts a `Kind` as part of a Step; instead it uses the top-level Kind of the subcommand. If this matches how you were already using --exclude (e.g. `x test --exclude test::std`), simply remove the kind: `--exclude std`. If you were using a kind that did not match the top-level subcommand, please open an issue explaining why you wanted this feature.
-
-### Non-breaking changes
-
-- `x.py check` needs opt-in to check tests (--all-targets) [#77473](https://github.com/rust-lang/rust/pull/77473)
-- The default bootstrap profiles are now located at `bootstrap/defaults/config.$PROFILE.toml` (previously they were located at `bootstrap/defaults/config.toml.$PROFILE`) [#77558](https://github.com/rust-lang/rust/pull/77558)
-- If you have Rust already installed, `x.py` will now infer the host target
- from the default rust toolchain. [#78513](https://github.com/rust-lang/rust/pull/78513)
-- Add options for enabling overflow checks, one for std (`overflow-checks-std`) and one for everything else (`overflow-checks`). Both default to false.
-- Add llvm option `enable-warnings` to have control on llvm compilation warnings. Default to false.
-- Add `rpath` option in `target` section to support set rpath option for each target independently. [#111242](https://github.com/rust-lang/rust/pull/111242)
-
-
-## [Version 2] - 2020-09-25
-
-- `host` now defaults to the value of `build` in all cases
- + Previously `host` defaulted to an empty list when `target` was overridden, and to `build` otherwise
-
-### Non-breaking changes
-
-- Add `x.py setup` [#76631](https://github.com/rust-lang/rust/pull/76631)
-- Add a changelog for x.py [#76626](https://github.com/rust-lang/rust/pull/76626)
-- Optionally, download LLVM from CI on Linux and NixOS. This can be enabled with `download-ci-llvm = true` under `[llvm]`.
- + [#76439](https://github.com/rust-lang/rust/pull/76349)
- + [#76667](https://github.com/rust-lang/rust/pull/76667)
- + [#76708](https://github.com/rust-lang/rust/pull/76708)
-- Distribute rustc sources as part of `rustc-dev` [#76856](https://github.com/rust-lang/rust/pull/76856)
-- Make the default stage for x.py configurable [#76625](https://github.com/rust-lang/rust/pull/76625). This can be enabled with `build-stage = N`, `doc-stage = N`, etc.
-- Add a dedicated debug-logging option [#76588](https://github.com/rust-lang/rust/pull/76588). Previously, `debug-logging` could only be set with `debug-assertions`, slowing down the compiler more than necessary.
-- Add sample defaults for x.py [#76628](https://github.com/rust-lang/rust/pull/76628)
-- Add `--keep-stage-std`, which behaves like `keep-stage` but allows the stage
- 0 compiler artifacts (i.e., stage1/bin/rustc) to be rebuilt if changed
- [#77120](https://github.com/rust-lang/rust/pull/77120).
-- File locking is now used to avoid collisions between multiple running instances of `x.py` (e.g. when using `rust-analyzer` and `x.py` at the same time). Note that Solaris and possibly other non Unix and non Windows systems don't support it [#108607](https://github.com/rust-lang/rust/pull/108607). This might possibly lead to build data corruption.
-
-
-## [Version 1] - 2020-09-11
-
-This is the first changelog entry, and it does not attempt to be an exhaustive list of features in x.py.
-Instead, this documents the changes to bootstrap in the past 2 months.
-
-- Improve defaults in `x.py` [#73964](https://github.com/rust-lang/rust/pull/73964)
- (see [blog post] for details)
-- Set `ninja = true` by default [#74922](https://github.com/rust-lang/rust/pull/74922)
-- Avoid trying to inversely cross-compile for build triple from host triples [#76415](https://github.com/rust-lang/rust/pull/76415)
-- Allow blessing expect-tests in tools [#75975](https://github.com/rust-lang/rust/pull/75975)
-- `x.py check` checks tests/examples/benches [#76258](https://github.com/rust-lang/rust/pull/76258)
-- Fix `rust.use-lld` when linker is not set [#76326](https://github.com/rust-lang/rust/pull/76326)
-- Build tests with LLD if `use-lld = true` was passed [#76378](https://github.com/rust-lang/rust/pull/76378)
-
-[blog post]: https://blog.rust-lang.org/inside-rust/2020/08/30/changes-to-x-py-defaults.html
diff --git a/src/bootstrap/Cargo.lock b/src/bootstrap/Cargo.lock
index f5220e361..57113b0ec 100644
--- a/src/bootstrap/Cargo.lock
+++ b/src/bootstrap/Cargo.lock
@@ -30,6 +30,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
+name = "bitflags"
+version = "2.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07"
+
+[[package]]
name = "block-buffer"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -50,6 +56,7 @@ dependencies = [
"fd-lock",
"filetime",
"hex",
+ "home",
"ignore",
"junction",
"libc",
@@ -104,40 +111,38 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "clap"
-version = "4.2.4"
+version = "4.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "956ac1f6381d8d82ab4684768f89c0ea3afe66925ceadb4eeb3fc452ffc55d62"
+checksum = "ac495e00dcec98c83465d5ad66c5c4fabd652fd6686e7c6269b117e729a6f17b"
dependencies = [
"clap_builder",
"clap_derive",
- "once_cell",
]
[[package]]
name = "clap_builder"
-version = "4.2.4"
+version = "4.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "84080e799e54cff944f4b4a4b0e71630b0e0443b25b985175c7dddc1a859b749"
+checksum = "c77ed9a32a62e6ca27175d00d29d05ca32e396ea1eb5fb01d8256b669cec7663"
dependencies = [
"anstyle",
- "bitflags",
"clap_lex",
]
[[package]]
name = "clap_complete"
-version = "4.2.2"
+version = "4.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "36774babb166352bb4f7b9cb16f781ffa3439d2a8f12cd31bea85a38c888fea3"
+checksum = "e3ae8ba90b9d8b007efe66e55e48fb936272f5ca00349b5b0e89877520d35ea7"
dependencies = [
"clap",
]
[[package]]
name = "clap_derive"
-version = "4.2.0"
+version = "4.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3f9644cd56d6b87dbe899ef8b053e331c0637664e9e21a33dfcdc36093f5c5c4"
+checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442"
dependencies = [
"heck",
"proc-macro2",
@@ -147,9 +152,9 @@ dependencies = [
[[package]]
name = "clap_lex"
-version = "0.4.1"
+version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8a2dd5a6fe8c6e3502f568a6353e5273bbb15193ad9a89e457b9970798efbea1"
+checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1"
[[package]]
name = "cmake"
@@ -176,16 +181,6 @@ dependencies = [
]
[[package]]
-name = "crossbeam-channel"
-version = "0.5.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521"
-dependencies = [
- "cfg-if",
- "crossbeam-utils",
-]
-
-[[package]]
name = "crossbeam-deque"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -252,30 +247,19 @@ checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
[[package]]
name = "errno"
-version = "0.3.0"
+version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "50d6a0976c999d473fe89ad888d5a284e55366d9dc9038b1ba2aa15128c4afa0"
+checksum = "ac3e13f66a2f95e32a39eaa81f6b95d42878ca0e1db0c7543723dfe12557e860"
dependencies = [
- "errno-dragonfly",
"libc",
"windows-sys",
]
[[package]]
-name = "errno-dragonfly"
-version = "0.1.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
-dependencies = [
- "cc",
- "libc",
-]
-
-[[package]]
name = "fd-lock"
-version = "3.0.11"
+version = "3.0.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9799aefb4a2e4a01cc47610b1dd47c18ab13d991f27bbcaed9296f5a53d5cbad"
+checksum = "ef033ed5e9bad94e55838ca0ca906db0e043f517adda0c8b79c7a8c66c93c1b5"
dependencies = [
"cfg-if",
"rustix",
@@ -330,27 +314,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
[[package]]
-name = "hermit-abi"
-version = "0.1.19"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
-dependencies = [
- "libc",
-]
-
-[[package]]
-name = "hermit-abi"
-version = "0.3.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b"
-
-[[package]]
name = "hex"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
[[package]]
+name = "home"
+version = "0.5.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "747309b4b440c06d57b0b25f2aee03ee9b5e5397d288c60e21fc709bb98a7408"
+dependencies = [
+ "winapi",
+]
+
+[[package]]
name = "ignore"
version = "0.4.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -369,17 +347,6 @@ dependencies = [
]
[[package]]
-name = "io-lifetimes"
-version = "1.0.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "09270fd4fa1111bc614ed2246c7ef56239a3063d5be0d1ec3b589c505d400aeb"
-dependencies = [
- "hermit-abi 0.3.2",
- "libc",
- "windows-sys",
-]
-
-[[package]]
name = "itoa"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -403,15 +370,15 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
-version = "0.2.140"
+version = "0.2.149"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c"
+checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b"
[[package]]
name = "linux-raw-sys"
-version = "0.3.2"
+version = "0.4.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3f508063cc7bb32987c71511216bd5a32be15bccb6a80b52df8b9d7f01fc3aa2"
+checksum = "da2479e8c062e40bf0066ffa0bc823de0a9368974af99c9f6df941d2c231e03f"
[[package]]
name = "log"
@@ -458,16 +425,6 @@ dependencies = [
]
[[package]]
-name = "num_cpus"
-version = "1.13.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1"
-dependencies = [
- "hermit-abi 0.1.19",
- "libc",
-]
-
-[[package]]
name = "object"
version = "0.32.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -528,25 +485,22 @@ dependencies = [
[[package]]
name = "rayon"
-version = "1.6.0"
+version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e060280438193c554f654141c9ea9417886713b7acd75974c85b18a69a88e0b"
+checksum = "9c27db03db7734835b3f53954b534c91069375ce6ccaa2e065441e07d9b6cdb1"
dependencies = [
- "crossbeam-deque",
"either",
"rayon-core",
]
[[package]]
name = "rayon-core"
-version = "1.10.1"
+version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cac410af5d00ab6884528b4ab69d1e8e146e8d471201800fa1b4524126de6ad3"
+checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed"
dependencies = [
- "crossbeam-channel",
"crossbeam-deque",
"crossbeam-utils",
- "num_cpus",
]
[[package]]
@@ -555,7 +509,7 @@ version = "0.2.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42"
dependencies = [
- "bitflags",
+ "bitflags 1.3.2",
]
[[package]]
@@ -583,13 +537,12 @@ checksum = "49b3de9ec5dc0a3417da371aab17d729997c15010e7fd24ff707773a33bddb64"
[[package]]
name = "rustix"
-version = "0.37.6"
+version = "0.38.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d097081ed288dfe45699b72f5b5d648e5f15d64d900c7080273baa20c16a6849"
+checksum = "745ecfa778e66b2b63c88a61cb36e0eea109e803b0b86bf9879fbc77c70e86ed"
dependencies = [
- "bitflags",
+ "bitflags 2.4.1",
"errno",
- "io-lifetimes",
"libc",
"linux-raw-sys",
"windows-sys",
@@ -787,27 +740,37 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows"
-version = "0.46.0"
+version = "0.51.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ca229916c5ee38c2f2bc1e9d8f04df975b4bd93f9955dc69fabb5d91270045c9"
+dependencies = [
+ "windows-core",
+ "windows-targets",
+]
+
+[[package]]
+name = "windows-core"
+version = "0.51.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cdacb41e6a96a052c6cb63a144f24900236121c6f63f4f8219fef5977ecb0c25"
+checksum = "f1f8cf84f35d2db49a46868f947758c7a1138116f7fac3bc844f43ade1292e64"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-sys"
-version = "0.45.0"
+version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
+checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-targets"
-version = "0.42.2"
+version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
+checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
@@ -820,45 +783,45 @@ dependencies = [
[[package]]
name = "windows_aarch64_gnullvm"
-version = "0.42.2"
+version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
+checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
[[package]]
name = "windows_aarch64_msvc"
-version = "0.42.2"
+version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
+checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
[[package]]
name = "windows_i686_gnu"
-version = "0.42.2"
+version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
+checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
[[package]]
name = "windows_i686_msvc"
-version = "0.42.2"
+version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
+checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
[[package]]
name = "windows_x86_64_gnu"
-version = "0.42.2"
+version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
+checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
[[package]]
name = "windows_x86_64_gnullvm"
-version = "0.42.2"
+version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
+checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
[[package]]
name = "windows_x86_64_msvc"
-version = "0.42.2"
+version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
+checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
[[package]]
name = "xattr"
diff --git a/src/bootstrap/Cargo.toml b/src/bootstrap/Cargo.toml
index 9bf26948a..e4d359141 100644
--- a/src/bootstrap/Cargo.toml
+++ b/src/bootstrap/Cargo.toml
@@ -5,38 +5,48 @@ edition = "2021"
build = "build.rs"
default-run = "bootstrap"
+[features]
+build-metrics = ["sysinfo"]
+
[lib]
-path = "lib.rs"
+path = "src/lib.rs"
doctest = false
[[bin]]
name = "bootstrap"
-path = "bin/main.rs"
+path = "src/bin/main.rs"
test = false
[[bin]]
name = "rustc"
-path = "bin/rustc.rs"
+path = "src/bin/rustc.rs"
test = false
[[bin]]
name = "rustdoc"
-path = "bin/rustdoc.rs"
+path = "src/bin/rustdoc.rs"
test = false
[[bin]]
name = "sccache-plus-cl"
-path = "bin/sccache-plus-cl.rs"
+path = "src/bin/sccache-plus-cl.rs"
test = false
[dependencies]
build_helper = { path = "../tools/build_helper" }
+cc = "1.0.69"
+clap = { version = "4.4.7", default-features = false, features = ["std", "usage", "help", "derive", "error-context"] }
+clap_complete = "4.4.3"
cmake = "0.1.38"
filetime = "0.2"
-cc = "1.0.69"
-libc = "0.2"
hex = "0.4"
+home = "0.5.4"
+ignore = "0.4.10"
+libc = "0.2"
object = { version = "0.32.0", default-features = false, features = ["archive", "coff", "read_core", "unaligned"] }
+once_cell = "1.7.2"
+opener = "0.5"
+semver = "1.0.17"
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.
@@ -46,27 +56,21 @@ sha2 = "0.10"
tar = "0.4"
termcolor = "1.2.0"
toml = "0.5"
-ignore = "0.4.10"
-opener = "0.5"
-once_cell = "1.7.2"
-xz2 = "0.1"
walkdir = "2"
+xz2 = "0.1"
# Dependencies needed by the build-metrics feature
sysinfo = { version = "0.26.0", optional = true }
-clap = { version = "4.2.4", default-features = false, features = ["std", "usage", "help", "derive", "error-context"] }
-clap_complete = "4.2.2"
-semver = "1.0.17"
# Solaris doesn't support flock() and thus fd-lock is not option now
[target.'cfg(not(target_os = "solaris"))'.dependencies]
-fd-lock = "3.0.8"
+fd-lock = "3.0.13"
[target.'cfg(windows)'.dependencies.junction]
version = "1.0.0"
[target.'cfg(windows)'.dependencies.windows]
-version = "0.46.0"
+version = "0.51.1"
features = [
"Win32_Foundation",
"Win32_Security",
@@ -80,9 +84,6 @@ features = [
[dev-dependencies]
pretty_assertions = "1.4"
-[features]
-build-metrics = ["sysinfo"]
-
# We care a lot about bootstrap's compile times, so don't include debuginfo for
# dependencies, only bootstrap itself.
[profile.dev]
diff --git a/src/bootstrap/README.md b/src/bootstrap/README.md
index 548281ca5..e7998a40a 100644
--- a/src/bootstrap/README.md
+++ b/src/bootstrap/README.md
@@ -181,11 +181,10 @@ Some general areas that you may be interested in modifying are:
`Config` struct.
* Adding a sanity check? Take a look at `bootstrap/sanity.rs`.
-If you make a major change, please remember to:
+If you make a major change on bootstrap configuration, please remember to:
-+ Update `VERSION` in `src/bootstrap/main.rs`.
-* Update `changelog-seen = N` in `config.example.toml`.
-* Add an entry in `src/bootstrap/CHANGELOG.md`.
++ Update `CONFIG_CHANGE_HISTORY` in `src/bootstrap/lib.rs`.
+* Update `change-id = {pull-request-id}` in `config.example.toml`.
A 'major change' includes
@@ -193,7 +192,7 @@ A 'major change' includes
* A change in the default options.
Changes that do not affect contributors to the compiler or users
-building rustc from source don't need an update to `VERSION`.
+building rustc from source don't need an update to `CONFIG_CHANGE_HISTORY`.
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,
@@ -201,3 +200,8 @@ please file issues on the [Rust issue tracker][rust-issue-tracker].
[rust-bootstrap-zulip]: https://rust-lang.zulipchat.com/#narrow/stream/t-infra.2Fbootstrap
[rust-issue-tracker]: https://github.com/rust-lang/rust/issues
+
+## Changelog
+
+Because we do not release bootstrap with versions, we also do not maintain CHANGELOG files. To
+review the changes made to bootstrap, simply run `git log --no-merges --oneline -- src/bootstrap`.
diff --git a/src/bootstrap/bootstrap.py b/src/bootstrap/bootstrap.py
index fac0cdf20..5a84e37f8 100644
--- a/src/bootstrap/bootstrap.py
+++ b/src/bootstrap/bootstrap.py
@@ -211,7 +211,7 @@ def require(cmd, exit=True, exception=False):
if exception:
raise
elif exit:
- eprint("error: unable to run `{}`: {}".format(' '.join(cmd), exc))
+ eprint("ERROR: unable to run `{}`: {}".format(' '.join(cmd), exc))
eprint("Please make sure it's installed and in the path.")
sys.exit(1)
return None
@@ -681,7 +681,7 @@ class RustBuild(object):
answer = self._should_fix_bins_and_dylibs = get_answer()
if answer:
- eprint("info: You seem to be using Nix.")
+ eprint("INFO: You seem to be using Nix.")
return answer
def fix_bin_or_dylib(self, fname):
@@ -727,7 +727,7 @@ class RustBuild(object):
"nix-build", "-E", nix_expr, "-o", nix_deps_dir,
])
except subprocess.CalledProcessError as reason:
- eprint("warning: failed to call nix-build:", reason)
+ eprint("WARNING: failed to call nix-build:", reason)
return
self.nix_deps_dir = nix_deps_dir
@@ -747,7 +747,7 @@ class RustBuild(object):
try:
subprocess.check_output([patchelf] + patchelf_args + [fname])
except subprocess.CalledProcessError as reason:
- eprint("warning: failed to call patchelf:", reason)
+ eprint("WARNING: failed to call patchelf:", reason)
return
def rustc_stamp(self):
@@ -954,6 +954,13 @@ class RustBuild(object):
if deny_warnings:
env["RUSTFLAGS"] += " -Dwarnings"
+ # Add RUSTFLAGS_BOOTSTRAP to RUSTFLAGS for bootstrap compilation.
+ # Note that RUSTFLAGS_BOOTSTRAP should always be added to the end of
+ # RUSTFLAGS to be actually effective (e.g., if we have `-Dwarnings` in
+ # RUSTFLAGS, passing `-Awarnings` from RUSTFLAGS_BOOTSTRAP should override it).
+ if "RUSTFLAGS_BOOTSTRAP" in env:
+ env["RUSTFLAGS"] += " " + env["RUSTFLAGS_BOOTSTRAP"]
+
env["PATH"] = os.path.join(self.bin_root(), "bin") + \
os.pathsep + env["PATH"]
if not os.path.isfile(self.cargo()):
@@ -998,7 +1005,7 @@ class RustBuild(object):
if 'SUDO_USER' in os.environ and not self.use_vendored_sources:
if os.getuid() == 0:
self.use_vendored_sources = True
- eprint('info: looks like you\'re trying to run this command as root')
+ eprint('INFO: looks like you\'re trying to run this command as root')
eprint(' and so in order to preserve your $HOME this will now')
eprint(' use vendored sources by default.')
@@ -1010,14 +1017,14 @@ class RustBuild(object):
"--sync ./src/tools/rust-analyzer/Cargo.toml " \
"--sync ./compiler/rustc_codegen_cranelift/Cargo.toml " \
"--sync ./src/bootstrap/Cargo.toml "
- eprint('error: vendoring required, but vendor directory does not exist.')
+ eprint('ERROR: vendoring required, but vendor directory does not exist.')
eprint(' Run `cargo vendor {}` to initialize the '
'vendor directory.'.format(sync_dirs))
eprint('Alternatively, use the pre-vendored `rustc-src` dist component.')
raise Exception("{} not found".format(vendor_dir))
if not os.path.exists(cargo_dir):
- eprint('error: vendoring required, but .cargo/config does not exist.')
+ eprint('ERROR: vendoring required, but .cargo/config does not exist.')
raise Exception("{} not found".format(cargo_dir))
else:
if os.path.exists(cargo_dir):
@@ -1042,6 +1049,12 @@ def bootstrap(args):
"""Configure, fetch, build and run the initial bootstrap"""
rust_root = os.path.abspath(os.path.join(__file__, '../../..'))
+ if not os.path.exists(os.path.join(rust_root, '.git')) and \
+ os.path.exists(os.path.join(rust_root, '.github')):
+ eprint("warn: Looks like you are trying to bootstrap Rust from a source that is neither a "
+ "git clone nor distributed tarball.\nThis build may fail due to missing submodules "
+ "unless you put them in place manually.")
+
# Read from `--config`, then `RUST_BOOTSTRAP_CONFIG`, then `./config.toml`,
# then `config.toml` in the root directory.
toml_path = args.config or os.getenv('RUST_BOOTSTRAP_CONFIG')
@@ -1112,7 +1125,7 @@ def main():
# process has to happen before anything is printed out.
if help_triggered:
eprint(
- "info: Downloading and building bootstrap before processing --help command.\n"
+ "INFO: Downloading and building bootstrap before processing --help command.\n"
" See src/bootstrap/README.md for help with common commands.")
exit_code = 0
diff --git a/src/bootstrap/bootstrap_test.py b/src/bootstrap/bootstrap_test.py
index dc06a4c97..6da410ed2 100644
--- a/src/bootstrap/bootstrap_test.py
+++ b/src/bootstrap/bootstrap_test.py
@@ -34,7 +34,7 @@ def serialize_and_parse(configure_args, bootstrap_args=None):
# Verify this is actually valid TOML.
tomllib.loads(build.config_toml)
except ImportError:
- print("warning: skipping TOML validation, need at least python 3.11", file=sys.stderr)
+ print("WARNING: skipping TOML validation, need at least python 3.11", file=sys.stderr)
return build
@@ -103,7 +103,6 @@ class GenerateAndParseConfig(unittest.TestCase):
"""Test that we can serialize and deserialize a config.toml file"""
def test_no_args(self):
build = serialize_and_parse([])
- self.assertEqual(build.get_toml("changelog-seen"), '2')
self.assertEqual(build.get_toml("profile"), 'dist')
self.assertIsNone(build.get_toml("llvm.download-ci-llvm"))
diff --git a/src/bootstrap/configure.py b/src/bootstrap/configure.py
index f469dbea6..544a42d9a 100755
--- a/src/bootstrap/configure.py
+++ b/src/bootstrap/configure.py
@@ -59,6 +59,7 @@ o("missing-tools", "dist.missing-tools", "allow failures when building tools")
o("use-libcxx", "llvm.use-libcxx", "build LLVM with libc++")
o("control-flow-guard", "rust.control-flow-guard", "Enable Control Flow Guard")
o("patch-binaries-for-nix", "build.patch-binaries-for-nix", "whether patch binaries for usage with Nix toolchains")
+o("new-symbol-mangling", "rust.new-symbol-mangling", "use symbol-mangling-version v0")
v("llvm-cflags", "llvm.cflags", "build LLVM with these extra compiler flags")
v("llvm-cxxflags", "llvm.cxxflags", "build LLVM with these extra compiler flags")
@@ -97,20 +98,7 @@ v("llvm-root", None, "set LLVM root")
v("llvm-config", None, "set path to llvm-config")
v("llvm-filecheck", None, "set path to LLVM's FileCheck utility")
v("python", "build.python", "set path to python")
-v("android-cross-path", "target.arm-linux-androideabi.android-ndk",
- "Android NDK standalone path (deprecated)")
-v("i686-linux-android-ndk", "target.i686-linux-android.android-ndk",
- "i686-linux-android NDK standalone path")
-v("arm-linux-androideabi-ndk", "target.arm-linux-androideabi.android-ndk",
- "arm-linux-androideabi NDK standalone path")
-v("armv7-linux-androideabi-ndk", "target.armv7-linux-androideabi.android-ndk",
- "armv7-linux-androideabi NDK standalone path")
-v("thumbv7neon-linux-androideabi-ndk", "target.thumbv7neon-linux-androideabi.android-ndk",
- "thumbv7neon-linux-androideabi NDK standalone path")
-v("aarch64-linux-android-ndk", "target.aarch64-linux-android.android-ndk",
- "aarch64-linux-android NDK standalone path")
-v("x86_64-linux-android-ndk", "target.x86_64-linux-android.android-ndk",
- "x86_64-linux-android NDK standalone path")
+v("android-ndk", "build.android-ndk", "set path to Android NDK")
v("musl-root", "target.x86_64-unknown-linux-musl.musl-root",
"MUSL root installation directory (deprecated)")
v("musl-root-x86_64", "target.x86_64-unknown-linux-musl.musl-root",
@@ -265,7 +253,7 @@ def parse_args(args):
if not found:
unknown_args.append(arg)
- # Note: here and a few other places, we use [-1] to apply the *last* value
+ # NOTE: here and a few other places, we use [-1] to apply the *last* value
# passed. But if option-checking is enabled, then the known_args loop will
# also assert that options are only passed once.
option_checking = ('option-checking' not in known_args
@@ -489,7 +477,7 @@ def configure_section(lines, config):
# These are used by rpm, but aren't accepted by x.py.
# Give a warning that they're ignored, but not a hard error.
if key in ["infodir", "localstatedir"]:
- print("warning: {} will be ignored".format(key))
+ print("WARNING: {} will be ignored".format(key))
else:
raise RuntimeError("failed to find config line for {}".format(key))
diff --git a/src/bootstrap/defaults/config.codegen.toml b/src/bootstrap/defaults/config.codegen.toml
index 113df88d7..7c33ce958 100644
--- a/src/bootstrap/defaults/config.codegen.toml
+++ b/src/bootstrap/defaults/config.codegen.toml
@@ -10,7 +10,7 @@ assertions = true
# enable warnings during the llvm compilation
enable-warnings = true
# build llvm from source
-download-ci-llvm = false
+download-ci-llvm = "if-unchanged"
[rust]
# This enables `RUSTC_LOG=debug`, avoiding confusing situations
diff --git a/src/bootstrap/download-ci-llvm-stamp b/src/bootstrap/download-ci-llvm-stamp
index ffc380579..9998fe2f5 100644
--- a/src/bootstrap/download-ci-llvm-stamp
+++ b/src/bootstrap/download-ci-llvm-stamp
@@ -1,4 +1,4 @@
Change this file to make users of the `download-ci-llvm` configuration download
a new version of LLVM from CI, even if the LLVM submodule hasn’t changed.
-Last change is for: https://github.com/rust-lang/rust/pull/113996
+Last change is for: https://github.com/rust-lang/rust/pull/116881
diff --git a/src/bootstrap/job.rs b/src/bootstrap/job.rs
deleted file mode 100644
index b0a97b540..000000000
--- a/src/bootstrap/job.rs
+++ /dev/null
@@ -1,143 +0,0 @@
-//! Job management on Windows for bootstrapping
-//!
-//! Most of the time when you're running a build system (e.g., make) you expect
-//! Ctrl-C or abnormal termination to actually terminate the entire tree of
-//! process in play, not just the one at the top. This currently works "by
-//! default" on Unix platforms because Ctrl-C actually sends a signal to the
-//! *process group* rather than the parent process, so everything will get torn
-//! down. On Windows, however, this does not happen and Ctrl-C just kills the
-//! parent process.
-//!
-//! To achieve the same semantics on Windows we use Job Objects to ensure that
-//! all processes die at the same time. Job objects have a mode of operation
-//! where when all handles to the object are closed it causes all child
-//! processes associated with the object to be terminated immediately.
-//! Conveniently whenever a process in the job object spawns a new process the
-//! child will be associated with the job object as well. This means if we add
-//! ourselves to the job object we create then everything will get torn down!
-//!
-//! Unfortunately most of the time the build system is actually called from a
-//! python wrapper (which manages things like building the build system) so this
-//! all doesn't quite cut it so far. To go the last mile we duplicate the job
-//! object handle into our parent process (a python process probably) and then
-//! close our own handle. This means that the only handle to the job object
-//! resides in the parent python process, so when python dies the whole build
-//! system dies (as one would probably expect!).
-//!
-//! Note that this module has a #[cfg(windows)] above it as none of this logic
-//! is required on Unix.
-
-use crate::Build;
-use std::env;
-use std::ffi::c_void;
-use std::io;
-use std::mem;
-
-use windows::{
- core::PCWSTR,
- Win32::Foundation::{CloseHandle, DuplicateHandle, DUPLICATE_SAME_ACCESS, HANDLE},
- Win32::System::Diagnostics::Debug::{SetErrorMode, SEM_NOGPFAULTERRORBOX, THREAD_ERROR_MODE},
- Win32::System::JobObjects::{
- AssignProcessToJobObject, CreateJobObjectW, JobObjectExtendedLimitInformation,
- SetInformationJobObject, JOBOBJECT_EXTENDED_LIMIT_INFORMATION,
- JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE, JOB_OBJECT_LIMIT_PRIORITY_CLASS,
- },
- Win32::System::Threading::{
- GetCurrentProcess, OpenProcess, BELOW_NORMAL_PRIORITY_CLASS, PROCESS_DUP_HANDLE,
- },
-};
-
-pub unsafe fn setup(build: &mut Build) {
- // Enable the Windows Error Reporting dialog which msys disables,
- // so we can JIT debug rustc
- let mode = SetErrorMode(THREAD_ERROR_MODE::default());
- let mode = THREAD_ERROR_MODE(mode);
- SetErrorMode(mode & !SEM_NOGPFAULTERRORBOX);
-
- // Create a new job object for us to use
- let job = CreateJobObjectW(None, PCWSTR::null()).unwrap();
-
- // Indicate that when all handles to the job object are gone that all
- // process in the object should be killed. Note that this includes our
- // entire process tree by default because we've added ourselves and our
- // children will reside in the job by default.
- let mut info = JOBOBJECT_EXTENDED_LIMIT_INFORMATION::default();
- info.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
- if build.config.low_priority {
- info.BasicLimitInformation.LimitFlags |= JOB_OBJECT_LIMIT_PRIORITY_CLASS;
- info.BasicLimitInformation.PriorityClass = BELOW_NORMAL_PRIORITY_CLASS.0;
- }
- let r = SetInformationJobObject(
- job,
- JobObjectExtendedLimitInformation,
- &info as *const _ as *const c_void,
- mem::size_of_val(&info) as u32,
- )
- .ok();
- assert!(r.is_ok(), "{}", io::Error::last_os_error());
-
- // Assign our process to this job object. Note that if this fails, one very
- // likely reason is that we are ourselves already in a job object! This can
- // happen on the build bots that we've got for Windows, or if just anyone
- // else is instrumenting the build. In this case we just bail out
- // immediately and assume that they take care of it.
- //
- // Also note that nested jobs (why this might fail) are supported in recent
- // versions of Windows, but the version of Windows that our bots are running
- // at least don't support nested job objects.
- let r = AssignProcessToJobObject(job, GetCurrentProcess()).ok();
- if r.is_err() {
- CloseHandle(job);
- return;
- }
-
- // If we've got a parent process (e.g., the python script that called us)
- // then move ownership of this job object up to them. That way if the python
- // script is killed (e.g., via ctrl-c) then we'll all be torn down.
- //
- // If we don't have a parent (e.g., this was run directly) then we
- // intentionally leak the job object handle. When our process exits
- // (normally or abnormally) it will close the handle implicitly, causing all
- // processes in the job to be cleaned up.
- let pid = match env::var("BOOTSTRAP_PARENT_ID") {
- Ok(s) => s,
- Err(..) => return,
- };
-
- let parent = match OpenProcess(PROCESS_DUP_HANDLE, false, pid.parse().unwrap()).ok() {
- Some(parent) => parent,
- _ => {
- // If we get a null parent pointer here, it is possible that either
- // we have an invalid pid or the parent process has been closed.
- // Since the first case rarely happens
- // (only when wrongly setting the environmental variable),
- // it might be better to improve the experience of the second case
- // when users have interrupted the parent process and we haven't finish
- // duplicating the handle yet. We just need close the job object if that occurs.
- CloseHandle(job);
- return;
- }
- };
-
- let mut parent_handle = HANDLE::default();
- let r = DuplicateHandle(
- GetCurrentProcess(),
- job,
- parent,
- &mut parent_handle,
- 0,
- false,
- DUPLICATE_SAME_ACCESS,
- )
- .ok();
-
- // If this failed, well at least we tried! An example of DuplicateHandle
- // failing in the past has been when the wrong python2 package spawned this
- // build system (e.g., the `python2` package in MSYS instead of
- // `mingw-w64-x86_64-python2`). Not sure why it failed, but the "failure
- // mode" here is that we only clean everything up when the build system
- // dies, not when the python parent does, so not too bad.
- if r.is_err() {
- CloseHandle(job);
- }
-}
diff --git a/src/bootstrap/bin/main.rs b/src/bootstrap/src/bin/main.rs
index c497cabbd..0a6072ae1 100644
--- a/src/bootstrap/bin/main.rs
+++ b/src/bootstrap/src/bin/main.rs
@@ -13,7 +13,7 @@ use std::{env, fs};
#[cfg(all(any(unix, windows), not(target_os = "solaris")))]
use bootstrap::t;
-use bootstrap::{Build, Config, Subcommand, VERSION};
+use bootstrap::{find_recent_config_change_ids, Build, Config, Subcommand, CONFIG_CHANGE_HISTORY};
fn main() {
let args = env::args().skip(1).collect::<Vec<_>>();
@@ -42,7 +42,7 @@ fn main() {
}
err => {
drop(err);
- println!("warning: build directory locked by process {pid}, waiting for lock");
+ println!("WARNING: build directory locked by process {pid}, waiting for lock");
let mut lock = t!(build_lock.write());
t!(lock.write(&process::id().to_string().as_ref()));
lock
@@ -51,7 +51,7 @@ fn main() {
}
#[cfg(any(not(any(unix, windows)), target_os = "solaris"))]
- println!("warning: file locking not supported for target, not locking build directory");
+ println!("WARNING: file locking not supported for target, not locking build directory");
// check_version warnings are not printed during setup
let changelog_suggestion =
@@ -61,9 +61,9 @@ fn main() {
// changelog warning, not the `x.py setup` message.
let suggest_setup = config.config.is_none() && !matches!(config.cmd, Subcommand::Setup { .. });
if suggest_setup {
- println!("warning: you have not made a `config.toml`");
+ println!("WARNING: you have not made a `config.toml`");
println!(
- "help: consider running `./x.py setup` or copying `config.example.toml` by running \
+ "HELP: consider running `./x.py setup` or copying `config.example.toml` by running \
`cp config.example.toml config.toml`"
);
} else if let Some(suggestion) = &changelog_suggestion {
@@ -74,9 +74,9 @@ fn main() {
Build::new(config).build();
if suggest_setup {
- println!("warning: you have not made a `config.toml`");
+ println!("WARNING: you have not made a `config.toml`");
println!(
- "help: consider running `./x.py setup` or copying `config.example.toml` by running \
+ "HELP: consider running `./x.py setup` or copying `config.example.toml` by running \
`cp config.example.toml config.toml`"
);
} else if let Some(suggestion) = &changelog_suggestion {
@@ -91,34 +91,53 @@ fn main() {
contents.contains("https://github.com/rust-lang/rust/issues/77620#issuecomment-705144570")
}) {
println!(
- "warning: You have the pre-push script installed to .git/hooks/pre-commit. \
+ "WARNING: You have the pre-push script installed to .git/hooks/pre-commit. \
Consider moving it to .git/hooks/pre-push instead, which runs less often."
);
}
if suggest_setup || changelog_suggestion.is_some() {
- println!("note: this message was printed twice to make it more likely to be seen");
+ println!("NOTE: this message was printed twice to make it more likely to be seen");
}
}
fn check_version(config: &Config) -> Option<String> {
let mut msg = String::new();
- let suggestion = if let Some(seen) = config.changelog_seen {
- if seen != VERSION {
- msg.push_str("warning: there have been changes to x.py since you last updated.\n");
- format!("update `config.toml` to use `changelog-seen = {VERSION}` instead")
- } else {
+ if config.changelog_seen.is_some() {
+ msg.push_str("WARNING: The use of `changelog-seen` is deprecated. Please refer to `change-id` option in `config.example.toml` instead.\n");
+ }
+
+ let latest_config_id = CONFIG_CHANGE_HISTORY.last().unwrap();
+ if let Some(id) = config.change_id {
+ if &id == latest_config_id {
return None;
}
+
+ let change_links: Vec<String> = find_recent_config_change_ids(id)
+ .iter()
+ .map(|id| format!("https://github.com/rust-lang/rust/pull/{id}"))
+ .collect();
+ if !change_links.is_empty() {
+ msg.push_str("WARNING: there have been changes to x.py since you last updated.\n");
+ msg.push_str("To see more detail about these changes, visit the following PRs:\n");
+
+ for link in change_links {
+ msg.push_str(&format!(" - {link}\n"));
+ }
+
+ msg.push_str("WARNING: there have been changes to x.py since you last updated.\n");
+
+ msg.push_str("NOTE: to silence this warning, ");
+ msg.push_str(&format!(
+ "update `config.toml` to use `change-id = {latest_config_id}` instead"
+ ));
+ }
} else {
- msg.push_str("warning: x.py has made several changes recently you may want to look at\n");
- format!("add `changelog-seen = {VERSION}` at the top of `config.toml`")
+ msg.push_str("WARNING: The `change-id` is missing in the `config.toml`. This means that you will not be able to track the major changes made to the bootstrap configurations.\n");
+ msg.push_str("NOTE: to silence this warning, ");
+ msg.push_str(&format!("add `change-id = {latest_config_id}` at the top of `config.toml`"));
};
- msg.push_str("help: consider looking at the changes in `src/bootstrap/CHANGELOG.md`\n");
- msg.push_str("note: to silence this warning, ");
- msg.push_str(&suggestion);
-
Some(msg)
}
diff --git a/src/bootstrap/bin/rustc.rs b/src/bootstrap/src/bin/rustc.rs
index 20cd63b96..070a2da6a 100644
--- a/src/bootstrap/bin/rustc.rs
+++ b/src/bootstrap/src/bin/rustc.rs
@@ -15,20 +15,27 @@
//! switching compilers for the bootstrap and for build scripts will probably
//! never get replaced.
-include!("../dylib_util.rs");
-include!("./_helper.rs");
-
use std::env;
use std::path::PathBuf;
-use std::process::{exit, Child, Command};
+use std::process::{Child, Command};
use std::time::Instant;
+use dylib_util::{dylib_path, dylib_path_var};
+
+#[path = "../utils/bin_helpers.rs"]
+mod bin_helpers;
+
+#[path = "../utils/dylib.rs"]
+mod dylib_util;
+
fn main() {
let args = env::args_os().skip(1).collect::<Vec<_>>();
let arg = |name| args.windows(2).find(|args| args[0] == name).and_then(|args| args[1].to_str());
- let stage = parse_rustc_stage();
- let verbose = parse_rustc_verbose();
+ // We don't use the stage in this shim, but let's parse it to make sure that we're invoked
+ // by bootstrap, or that we provide a helpful error message if not.
+ bin_helpers::parse_rustc_stage();
+ let verbose = bin_helpers::parse_rustc_verbose();
// Detect whether or not we're a build script depending on whether --target
// is passed (a bit janky...)
@@ -108,41 +115,25 @@ fn main() {
cmd.arg("-Ztls-model=initial-exec");
}
} else {
- // FIXME(rust-lang/cargo#5754) we shouldn't be using special env vars
- // here, but rather Cargo should know what flags to pass rustc itself.
-
- // Override linker if necessary.
- if let Ok(host_linker) = env::var("RUSTC_HOST_LINKER") {
- cmd.arg(format!("-Clinker={host_linker}"));
- }
- if env::var_os("RUSTC_HOST_FUSE_LD_LLD").is_some() {
- cmd.arg("-Clink-args=-fuse-ld=lld");
- }
-
- if let Ok(s) = env::var("RUSTC_HOST_CRT_STATIC") {
- if s == "true" {
- cmd.arg("-C").arg("target-feature=+crt-static");
- }
- if s == "false" {
- cmd.arg("-C").arg("target-feature=-crt-static");
+ // Find any host flags that were passed by bootstrap.
+ // The flags are stored in a RUSTC_HOST_FLAGS variable, separated by spaces.
+ if let Ok(flags) = std::env::var("RUSTC_HOST_FLAGS") {
+ for flag in flags.split(' ') {
+ cmd.arg(flag);
}
}
-
- // Cargo doesn't pass RUSTFLAGS to proc_macros:
- // https://github.com/rust-lang/cargo/issues/4423
- // Thus, if we are on stage 0, we explicitly set `--cfg=bootstrap`.
- // We also declare that the flag is expected, which we need to do to not
- // get warnings about it being unexpected.
- if stage == "0" {
- cmd.arg("--cfg=bootstrap");
- }
- cmd.arg("-Zunstable-options");
- cmd.arg("--check-cfg=values(bootstrap)");
}
if let Ok(map) = env::var("RUSTC_DEBUGINFO_MAP") {
cmd.arg("--remap-path-prefix").arg(&map);
}
+ // The remap flags for Cargo registry sources need to be passed after the remapping for the
+ // Rust source code directory, to handle cases when $CARGO_HOME is inside the source directory.
+ if let Ok(maps) = env::var("RUSTC_CARGO_REGISTRY_SRC_TO_REMAP") {
+ for map in maps.split('\t') {
+ cmd.arg("--remap-path-prefix").arg(map);
+ }
+ }
// Force all crates compiled by this compiler to (a) be unstable and (b)
// allow the `rustc_private` feature to link to other unstable crates
@@ -217,6 +208,12 @@ fn main() {
eprintln!("{prefix} libdir: {libdir:?}");
}
+ if env::var_os("RUSTC_BOLT_LINK_FLAGS").is_some() {
+ if let Some("rustc_driver") = crate_name {
+ cmd.arg("-Clink-args=-Wl,-q");
+ }
+ }
+
let start = Instant::now();
let (child, status) = {
let errmsg = format!("\nFailed to run:\n{cmd:?}\n-------------");
@@ -248,7 +245,7 @@ fn main() {
if status.success() {
std::process::exit(0);
- // note: everything below here is unreachable. do not put code that
+ // NOTE: everything below here is unreachable. do not put code that
// should run on success, after this block.
}
if verbose > 0 {
@@ -306,10 +303,9 @@ fn format_rusage_data(child: Child) -> Option<String> {
&mut user_filetime,
)
}
- .ok()
.ok()?;
- unsafe { FileTimeToSystemTime(&user_filetime, &mut user_time) }.ok().ok()?;
- unsafe { FileTimeToSystemTime(&kernel_filetime, &mut kernel_time) }.ok().ok()?;
+ unsafe { FileTimeToSystemTime(&user_filetime, &mut user_time) }.ok()?;
+ unsafe { FileTimeToSystemTime(&kernel_filetime, &mut kernel_time) }.ok()?;
// Unlike on Linux with RUSAGE_CHILDREN, this will only return memory information for the process
// with the given handle and none of that process's children.
diff --git a/src/bootstrap/bin/rustdoc.rs b/src/bootstrap/src/bin/rustdoc.rs
index 6561c1c19..dbbce6fe2 100644
--- a/src/bootstrap/bin/rustdoc.rs
+++ b/src/bootstrap/src/bin/rustdoc.rs
@@ -5,17 +5,21 @@
use std::env;
use std::ffi::OsString;
use std::path::PathBuf;
-use std::process::{exit, Command};
+use std::process::Command;
-include!("../dylib_util.rs");
+use dylib_util::{dylib_path, dylib_path_var};
-include!("./_helper.rs");
+#[path = "../utils/bin_helpers.rs"]
+mod bin_helpers;
+
+#[path = "../utils/dylib.rs"]
+mod dylib_util;
fn main() {
let args = env::args_os().skip(1).collect::<Vec<_>>();
- let stage = parse_rustc_stage();
- let verbose = parse_rustc_verbose();
+ let stage = bin_helpers::parse_rustc_stage();
+ let verbose = bin_helpers::parse_rustc_verbose();
let rustdoc = env::var_os("RUSTDOC_REAL").expect("RUSTDOC_REAL was not set");
let libdir = env::var_os("RUSTDOC_LIBDIR").expect("RUSTDOC_LIBDIR was not set");
@@ -66,7 +70,9 @@ fn main() {
cmd.arg("--cfg=bootstrap");
}
cmd.arg("-Zunstable-options");
+ // #[cfg(bootstrap)]
cmd.arg("--check-cfg=values(bootstrap)");
+ // cmd.arg("--check-cfg=cfg(bootstrap)");
if verbose > 1 {
eprintln!(
diff --git a/src/bootstrap/bin/sccache-plus-cl.rs b/src/bootstrap/src/bin/sccache-plus-cl.rs
index 554c2dd4d..554c2dd4d 100644
--- a/src/bootstrap/bin/sccache-plus-cl.rs
+++ b/src/bootstrap/src/bin/sccache-plus-cl.rs
diff --git a/src/bootstrap/check.rs b/src/bootstrap/src/core/build_steps/check.rs
index b417abc00..121925b56 100644
--- a/src/bootstrap/check.rs
+++ b/src/bootstrap/src/core/build_steps/check.rs
@@ -1,10 +1,12 @@
//! Implementation of compiling the compiler and standard library, in "check"-based modes.
-use crate::builder::{crate_description, Alias, Builder, Kind, RunConfig, ShouldRun, Step};
-use crate::cache::Interned;
-use crate::compile::{add_to_sysroot, run_cargo, rustc_cargo, rustc_cargo_env, std_cargo};
-use crate::config::TargetSelection;
-use crate::tool::{prepare_tool_cargo, SourceType};
+use crate::core::build_steps::compile::{
+ add_to_sysroot, run_cargo, rustc_cargo, rustc_cargo_env, std_cargo,
+};
+use crate::core::build_steps::tool::{prepare_tool_cargo, SourceType};
+use crate::core::builder::{crate_description, Alias, Builder, Kind, RunConfig, ShouldRun, Step};
+use crate::core::config::TargetSelection;
+use crate::utils::cache::Interned;
use crate::INTERNER;
use crate::{Compiler, Mode, Subcommand};
use std::path::{Path, PathBuf};
@@ -16,7 +18,7 @@ pub struct Std {
///
/// This shouldn't be used from other steps; see the comment on [`compile::Rustc`].
///
- /// [`compile::Rustc`]: crate::compile::Rustc
+ /// [`compile::Rustc`]: crate::core::build_steps::compile::Rustc
crates: Interned<Vec<String>>,
}
@@ -193,7 +195,7 @@ pub struct Rustc {
///
/// This shouldn't be used from other steps; see the comment on [`compile::Rustc`].
///
- /// [`compile::Rustc`]: crate::compile::Rustc
+ /// [`compile::Rustc`]: crate::core::build_steps::compile::Rustc
crates: Interned<Vec<String>>,
}
@@ -237,8 +239,8 @@ impl Step for Rustc {
// the sysroot for the compiler to find. Otherwise, we're going to
// fail when building crates that need to generate code (e.g., build
// scripts and their dependencies).
- builder.ensure(crate::compile::Std::new(compiler, compiler.host));
- builder.ensure(crate::compile::Std::new(compiler, target));
+ builder.ensure(crate::core::build_steps::compile::Std::new(compiler, compiler.host));
+ builder.ensure(crate::core::build_steps::compile::Std::new(compiler, target));
} else {
builder.ensure(Std::new(target));
}
@@ -387,7 +389,7 @@ impl Step for RustAnalyzer {
&["rust-analyzer/in-rust-tree".to_owned()],
);
- cargo.allow_features(crate::tool::RustAnalyzer::ALLOW_FEATURES);
+ cargo.allow_features(crate::core::build_steps::tool::RustAnalyzer::ALLOW_FEATURES);
// For ./x.py clippy, don't check those targets because
// linting tests and benchmarks can produce very noisy results
diff --git a/src/bootstrap/clean.rs b/src/bootstrap/src/core/build_steps/clean.rs
index 7389816b4..cbb6b5f46 100644
--- a/src/bootstrap/clean.rs
+++ b/src/bootstrap/src/core/build_steps/clean.rs
@@ -9,9 +9,9 @@ use std::fs;
use std::io::{self, ErrorKind};
use std::path::Path;
-use crate::builder::{crate_description, Builder, RunConfig, ShouldRun, Step};
-use crate::cache::Interned;
-use crate::util::t;
+use crate::core::builder::{crate_description, Builder, RunConfig, ShouldRun, Step};
+use crate::utils::cache::Interned;
+use crate::utils::helpers::t;
use crate::{Build, Compiler, Mode, Subcommand};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@@ -68,6 +68,12 @@ macro_rules! clean_crate_tree {
let compiler = self.compiler;
let target = compiler.host;
let mut cargo = builder.bare_cargo(compiler, $mode, target, "clean");
+
+ // Since https://github.com/rust-lang/rust/pull/111076 enables
+ // unstable cargo feature (`public-dependency`), we need to ensure
+ // that unstable features are enabled before reading libstd Cargo.toml.
+ cargo.env("RUSTC_BOOTSTRAP", "1");
+
for krate in &*self.crates {
cargo.arg("-p");
cargo.arg(krate);
@@ -139,7 +145,6 @@ fn clean_specific_stage(build: &Build, stage: u32) {
fn clean_default(build: &Build) {
rm_rf(&build.out.join("tmp"));
rm_rf(&build.out.join("dist"));
- rm_rf(&build.out.join("bootstrap"));
rm_rf(&build.out.join("rustfmt.stamp"));
for host in &build.hosts {
@@ -178,7 +183,7 @@ fn rm_rf(path: &Path) {
&& p.file_name().and_then(std::ffi::OsStr::to_str)
== Some("bootstrap.exe")
{
- eprintln!("warning: failed to delete '{}'.", p.display());
+ eprintln!("WARNING: failed to delete '{}'.", p.display());
return Ok(());
}
Err(e)
diff --git a/src/bootstrap/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs
index 292ccc578..7021a9543 100644
--- a/src/bootstrap/compile.rs
+++ b/src/bootstrap/src/core/build_steps/compile.rs
@@ -19,16 +19,17 @@ use std::str;
use serde_derive::Deserialize;
-use crate::builder::crate_description;
-use crate::builder::Cargo;
-use crate::builder::{Builder, Kind, PathSet, RunConfig, ShouldRun, Step, TaskPath};
-use crate::cache::{Interned, INTERNER};
-use crate::config::{DebuginfoLevel, LlvmLibunwind, RustcLto, TargetSelection};
-use crate::dist;
-use crate::llvm;
-use crate::tool::SourceType;
-use crate::util::get_clang_cl_resource_dir;
-use crate::util::{exe, is_debug_info, is_dylib, output, symlink_dir, t, up_to_date};
+use crate::core::build_steps::dist;
+use crate::core::build_steps::llvm;
+use crate::core::build_steps::tool::SourceType;
+use crate::core::builder::crate_description;
+use crate::core::builder::Cargo;
+use crate::core::builder::{Builder, Kind, PathSet, RunConfig, ShouldRun, Step, TaskPath};
+use crate::core::config::{DebuginfoLevel, LlvmLibunwind, RustcLto, TargetSelection};
+use crate::utils::cache::{Interned, INTERNER};
+use crate::utils::helpers::{
+ exe, get_clang_cl_resource_dir, is_debug_info, is_dylib, output, symlink_dir, t, up_to_date,
+};
use crate::LLVM_TOOLS;
use crate::{CLang, Compiler, DependencyType, GitRepo, Mode};
use filetime::FileTime;
@@ -44,15 +45,42 @@ pub struct Std {
/// When using download-rustc, we need to use a new build of `std` for running unit tests of Std itself,
/// but we need to use the downloaded copy of std for linking to rustdoc. Allow this to be overriden by `builder.ensure` from other steps.
force_recompile: bool,
+ extra_rust_args: &'static [&'static str],
}
impl Std {
pub fn new(compiler: Compiler, target: TargetSelection) -> Self {
- Self { target, compiler, crates: Default::default(), force_recompile: false }
+ Self {
+ target,
+ compiler,
+ crates: Default::default(),
+ force_recompile: false,
+ extra_rust_args: &[],
+ }
}
pub fn force_recompile(compiler: Compiler, target: TargetSelection) -> Self {
- Self { target, compiler, crates: Default::default(), force_recompile: true }
+ Self {
+ target,
+ compiler,
+ crates: Default::default(),
+ force_recompile: true,
+ extra_rust_args: &[],
+ }
+ }
+
+ pub fn new_with_extra_rust_args(
+ compiler: Compiler,
+ target: TargetSelection,
+ extra_rust_args: &'static [&'static str],
+ ) -> Self {
+ Self {
+ target,
+ compiler,
+ crates: Default::default(),
+ force_recompile: false,
+ extra_rust_args,
+ }
}
}
@@ -80,6 +108,7 @@ impl Step for Std {
target: run.target,
crates,
force_recompile: false,
+ extra_rust_args: &[],
});
}
@@ -112,7 +141,7 @@ impl Step for Std {
if builder.config.keep_stage.contains(&compiler.stage)
|| builder.config.keep_stage_std.contains(&compiler.stage)
{
- builder.info("Warning: Using a potentially old libstd. This may not behave well.");
+ builder.info("WARNING: Using a potentially old libstd. This may not behave well.");
copy_third_party_objects(builder, &compiler, target);
copy_self_contained_objects(builder, &compiler, target);
@@ -158,6 +187,25 @@ impl Step for Std {
target_deps.extend(copy_third_party_objects(builder, &compiler, target));
target_deps.extend(copy_self_contained_objects(builder, &compiler, target));
+ // The LLD wrappers and `rust-lld` are self-contained linking components that can be
+ // necessary to link the stdlib on some targets. We'll also need to copy these binaries to
+ // the `stage0-sysroot` to ensure the linker is found when bootstrapping on such a target.
+ if compiler.stage == 0 && compiler.host == builder.config.build {
+ // We want to copy the host `bin` folder within the `rustlib` folder in the sysroot.
+ let src_sysroot_bin = builder
+ .rustc_snapshot_sysroot()
+ .join("lib")
+ .join("rustlib")
+ .join(compiler.host.triple)
+ .join("bin");
+ if src_sysroot_bin.exists() {
+ let target_sysroot_bin =
+ builder.sysroot_libdir(compiler, target).parent().unwrap().join("bin");
+ t!(fs::create_dir_all(&target_sysroot_bin));
+ builder.cp_r(&src_sysroot_bin, &target_sysroot_bin);
+ }
+ }
+
let mut cargo = builder.cargo(compiler, Mode::Std, SourceType::InTree, target, "build");
std_cargo(builder, target, compiler.stage, &mut cargo);
for krate in &*self.crates {
@@ -168,6 +216,9 @@ impl Step for Std {
if target.is_synthetic() {
cargo.env("RUSTC_BOOTSTRAP_SYNTHETIC_TARGET", "1");
}
+ for rustflag in self.extra_rust_args.into_iter() {
+ cargo.rustflag(rustflag);
+ }
let _guard = builder.msg(
Kind::Build,
@@ -362,11 +413,6 @@ pub fn std_cargo(builder: &Builder<'_>, target: TargetSelection, stage: u32, car
let mut features = String::new();
- // Cranelift doesn't support `asm`.
- if stage != 0 && builder.config.default_codegen_backend().unwrap_or_default() == "cranelift" {
- features += " compiler-builtins-no-asm";
- }
-
if builder.no_std(target) == Some(true) {
features += " compiler-builtins-mem";
if !target.starts_with("bpf") {
@@ -491,7 +537,7 @@ impl Step for StdLink {
let (libdir, hostdir) = if self.force_recompile && builder.download_rustc() {
// NOTE: copies part of `sysroot_libdir` to avoid having to add a new `force_recompile` argument there too
let lib = builder.sysroot_libdir_relative(self.compiler);
- let sysroot = builder.ensure(crate::compile::Sysroot {
+ let sysroot = builder.ensure(crate::core::build_steps::compile::Sysroot {
compiler: self.compiler,
force_recompile: self.force_recompile,
});
@@ -771,8 +817,8 @@ impl Step for Rustc {
builder.ensure(Std::new(compiler, target));
if builder.config.keep_stage.contains(&compiler.stage) {
- builder.info("Warning: Using a potentially old librustc. This may not behave well.");
- builder.info("Warning: Use `--keep-stage-std` if you want to rebuild the compiler when it changes");
+ builder.info("WARNING: Using a potentially old librustc. This may not behave well.");
+ builder.info("WARNING: Use `--keep-stage-std` if you want to rebuild the compiler when it changes");
builder.ensure(RustcLink::from_rustc(self, compiler));
return;
}
@@ -887,6 +933,11 @@ impl Step for Rustc {
cargo.arg("-p").arg(krate);
}
+ if builder.build.config.enable_bolt_settings && compiler.stage == 1 {
+ // Relocations are required for BOLT to work.
+ cargo.env("RUSTC_BOLT_LINK_FLAGS", "1");
+ }
+
let _guard = builder.msg_sysroot_tool(
Kind::Build,
compiler.stage,
@@ -992,7 +1043,8 @@ pub fn rustc_cargo_env(
// detected that LLVM is already built and good to go which helps prevent
// busting caches (e.g. like #71152).
if builder.config.llvm_enabled() {
- let building_is_expensive = crate::llvm::prebuilt_llvm_config(builder, target).is_err();
+ let building_is_expensive =
+ crate::core::build_steps::llvm::prebuilt_llvm_config(builder, target).is_err();
// `top_stage == stage` might be false for `check --stage 1`, if we are building the stage 1 compiler
let can_skip_build = builder.kind == Kind::Check && builder.top_stage == stage;
let should_skip_build = building_is_expensive && can_skip_build;
@@ -1151,8 +1203,8 @@ fn is_codegen_cfg_needed(path: &TaskPath, run: &RunConfig<'_>) -> bool {
}
if needs_codegen_backend_config {
run.builder.info(
- "Warning: no codegen-backends config matched the requested path to build a codegen backend. \
- Help: add backend to codegen-backends in config.toml.",
+ "WARNING: no codegen-backends config matched the requested path to build a codegen backend. \
+ HELP: add backend to codegen-backends in config.toml.",
);
return true;
}
@@ -1198,7 +1250,7 @@ impl Step for CodegenBackend {
if builder.config.keep_stage.contains(&compiler.stage) {
builder.info(
- "Warning: Using a potentially old codegen backend. \
+ "WARNING: Using a potentially old codegen backend. \
This may not behave well.",
);
// Codegen backends are linked separately from this step today, so we don't do
@@ -1473,14 +1525,14 @@ impl Step for Sysroot {
let sysroot_lib_rustlib_src_rust = sysroot_lib_rustlib_src.join("rust");
if let Err(e) = symlink_dir(&builder.config, &builder.src, &sysroot_lib_rustlib_src_rust) {
eprintln!(
- "warning: creating symbolic link `{}` to `{}` failed with {}",
+ "WARNING: creating symbolic link `{}` to `{}` failed with {}",
sysroot_lib_rustlib_src_rust.display(),
builder.src.display(),
e,
);
if builder.config.rust_remap_debuginfo {
eprintln!(
- "warning: some `tests/ui` tests will fail when lacking `{}`",
+ "WARNING: some `tests/ui` tests will fail when lacking `{}`",
sysroot_lib_rustlib_src_rust.display(),
);
}
@@ -1493,7 +1545,7 @@ impl Step for Sysroot {
symlink_dir(&builder.config, &builder.src, &sysroot_lib_rustlib_rustcsrc_rust)
{
eprintln!(
- "warning: creating symbolic link `{}` to `{}` failed with {}",
+ "WARNING: creating symbolic link `{}` to `{}` failed with {}",
sysroot_lib_rustlib_rustcsrc_rust.display(),
builder.src.display(),
e,
@@ -1658,15 +1710,17 @@ impl Step for Assemble {
let src_exe = exe("lld", target_compiler.host);
let dst_exe = exe("rust-lld", target_compiler.host);
builder.copy(&lld_install.join("bin").join(&src_exe), &libdir_bin.join(&dst_exe));
- // for `-Z gcc-ld=lld`
- let gcc_ld_dir = libdir_bin.join("gcc-ld");
- t!(fs::create_dir(&gcc_ld_dir));
- let lld_wrapper_exe = builder.ensure(crate::tool::LldWrapper {
+ let self_contained_lld_dir = libdir_bin.join("gcc-ld");
+ t!(fs::create_dir(&self_contained_lld_dir));
+ let lld_wrapper_exe = builder.ensure(crate::core::build_steps::tool::LldWrapper {
compiler: build_compiler,
target: target_compiler.host,
});
for name in crate::LLD_FILE_NAMES {
- builder.copy(&lld_wrapper_exe, &gcc_ld_dir.join(exe(name, target_compiler.host)));
+ builder.copy(
+ &lld_wrapper_exe,
+ &self_contained_lld_dir.join(exe(name, target_compiler.host)),
+ );
}
}
@@ -1932,7 +1986,7 @@ pub fn stream_cargo(
builder.verbose(&format!("running: {cargo:?}"));
let mut child = match cargo.spawn() {
Ok(child) => child,
- Err(e) => panic!("failed to execute command: {cargo:?}\nerror: {e}"),
+ Err(e) => panic!("failed to execute command: {cargo:?}\nERROR: {e}"),
};
// Spawn Cargo slurping up its JSON output. We'll start building up the
@@ -1996,7 +2050,7 @@ pub fn strip_debug(builder: &Builder<'_>, target: TargetSelection, path: &Path)
}
let previous_mtime = FileTime::from_last_modification_time(&path.metadata().unwrap());
- // Note: `output` will propagate any errors here.
+ // NOTE: `output` will propagate any errors here.
output(Command::new("strip").arg("--strip-debug").arg(path));
// After running `strip`, we have to set the file modification time to what it was before,
diff --git a/src/bootstrap/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs
index 32da4ac29..c485481b9 100644
--- a/src/bootstrap/dist.rs
+++ b/src/bootstrap/src/core/build_steps/dist.rs
@@ -19,16 +19,16 @@ use std::process::Command;
use object::read::archive::ArchiveFile;
use object::BinaryFormat;
-use crate::builder::{Builder, Kind, RunConfig, ShouldRun, Step};
-use crate::cache::{Interned, INTERNER};
-use crate::channel;
-use crate::compile;
-use crate::config::TargetSelection;
-use crate::doc::DocumentationFormat;
-use crate::llvm;
-use crate::tarball::{GeneratedTarball, OverlayKind, Tarball};
-use crate::tool::{self, Tool};
-use crate::util::{exe, is_dylib, output, t, timeit};
+use crate::core::build_steps::compile;
+use crate::core::build_steps::doc::DocumentationFormat;
+use crate::core::build_steps::llvm;
+use crate::core::build_steps::tool::{self, Tool};
+use crate::core::builder::{Builder, Kind, RunConfig, ShouldRun, Step};
+use crate::core::config::TargetSelection;
+use crate::utils::cache::{Interned, INTERNER};
+use crate::utils::channel;
+use crate::utils::helpers::{exe, is_dylib, output, t, target_supports_cranelift_backend, timeit};
+use crate::utils::tarball::{GeneratedTarball, OverlayKind, Tarball};
use crate::{Compiler, DependencyType, Mode, LLVM_TOOLS};
pub fn pkgname(builder: &Builder<'_>, component: &str) -> String {
@@ -104,7 +104,7 @@ impl Step for JsonDocs {
/// Builds the `rust-docs-json` installer component.
fn run(self, builder: &Builder<'_>) -> Option<GeneratedTarball> {
let host = self.host;
- builder.ensure(crate::doc::Std::new(
+ builder.ensure(crate::core::build_steps::doc::Std::new(
builder.top_stage,
host,
builder,
@@ -443,19 +443,6 @@ impl Step for Rustc {
}
}
- // Copy over the codegen backends
- let backends_src = builder.sysroot_codegen_backends(compiler);
- let backends_rel = backends_src
- .strip_prefix(&src)
- .unwrap()
- .strip_prefix(builder.sysroot_libdir_relative(compiler))
- .unwrap();
- // Don't use custom libdir here because ^lib/ will be resolved again with installer
- let backends_dst = image.join("lib").join(&backends_rel);
-
- t!(fs::create_dir_all(&backends_dst));
- builder.cp_r(&backends_src, &backends_dst);
-
// Copy libLLVM.so to the lib dir as well, if needed. While not
// technically needed by rustc itself it's needed by lots of other
// components like the llvm tools and LLD. LLD is included below and
@@ -471,14 +458,15 @@ impl Step for Rustc {
let src_dir = builder.sysroot_libdir(compiler, host).parent().unwrap().join("bin");
let rust_lld = exe("rust-lld", compiler.host);
builder.copy(&src_dir.join(&rust_lld), &dst_dir.join(&rust_lld));
- // for `-Z gcc-ld=lld`
- let gcc_lld_src_dir = src_dir.join("gcc-ld");
- let gcc_lld_dst_dir = dst_dir.join("gcc-ld");
- t!(fs::create_dir(&gcc_lld_dst_dir));
+ let self_contained_lld_src_dir = src_dir.join("gcc-ld");
+ let self_contained_lld_dst_dir = dst_dir.join("gcc-ld");
+ t!(fs::create_dir(&self_contained_lld_dst_dir));
for name in crate::LLD_FILE_NAMES {
let exe_name = exe(name, compiler.host);
- builder
- .copy(&gcc_lld_src_dir.join(&exe_name), &gcc_lld_dst_dir.join(&exe_name));
+ builder.copy(
+ &self_contained_lld_src_dir.join(&exe_name),
+ &self_contained_lld_dst_dir.join(&exe_name),
+ );
}
}
@@ -487,7 +475,7 @@ impl Step for Rustc {
let man_src = builder.src.join("src/doc/man");
let man_dst = image.join("share/man/man1");
- // don't use our `bootstrap::util::{copy, cp_r}`, because those try
+ // don't use our `bootstrap::{copy, cp_r}`, because those try
// to hardlink, and we don't want to edit the source templates
for file_entry in builder.read_dir(&man_src) {
let page_src = file_entry.path();
@@ -1001,11 +989,15 @@ impl Step for PlainSourceTarball {
channel::write_commit_info_file(&plain_dst_src, info);
}
- // If we're building from git sources, we need to vendor a complete distribution.
- if builder.rust_info().is_managed_git_subrepository() {
- // Ensure we have the submodules checked out.
- builder.update_submodule(Path::new("src/tools/cargo"));
- builder.update_submodule(Path::new("src/tools/rust-analyzer"));
+ // If we're building from git or tarball sources, we need to vendor
+ // a complete distribution.
+ if builder.rust_info().is_managed_git_subrepository()
+ || builder.rust_info().is_from_tarball()
+ {
+ if builder.rust_info().is_managed_git_subrepository() {
+ // Ensure we have the submodules checked out.
+ builder.update_submodule(Path::new("src/tools/cargo"));
+ }
// Vendor all Cargo dependencies
let mut cmd = Command::new(&builder.initial_cargo);
@@ -1278,6 +1270,102 @@ impl Step for Miri {
}
#[derive(Debug, PartialOrd, Ord, Copy, Clone, Hash, PartialEq, Eq)]
+pub struct CodegenBackend {
+ pub compiler: Compiler,
+ pub backend: Interned<String>,
+}
+
+impl Step for CodegenBackend {
+ type Output = Option<GeneratedTarball>;
+ const DEFAULT: bool = true;
+ const ONLY_HOSTS: bool = true;
+
+ fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
+ run.path("compiler/rustc_codegen_cranelift")
+ }
+
+ fn make_run(run: RunConfig<'_>) {
+ for &backend in &run.builder.config.rust_codegen_backends {
+ if backend == "llvm" {
+ continue; // Already built as part of rustc
+ }
+
+ run.builder.ensure(CodegenBackend {
+ compiler: run.builder.compiler(run.builder.top_stage, run.target),
+ backend,
+ });
+ }
+ }
+
+ fn run(self, builder: &Builder<'_>) -> Option<GeneratedTarball> {
+ if builder.config.dry_run() {
+ return None;
+ }
+
+ // This prevents rustc_codegen_cranelift from being built for "dist"
+ // or "install" on the stable/beta channels. It is not yet stable and
+ // should not be included.
+ if !builder.build.unstable_features() {
+ return None;
+ }
+
+ if !builder.config.rust_codegen_backends.contains(&self.backend) {
+ return None;
+ }
+
+ if self.backend == "cranelift" {
+ if !target_supports_cranelift_backend(self.compiler.host) {
+ builder.info("target not supported by rustc_codegen_cranelift. skipping");
+ return None;
+ }
+
+ if self.compiler.host.contains("windows") {
+ builder.info(
+ "dist currently disabled for windows by rustc_codegen_cranelift. skipping",
+ );
+ return None;
+ }
+ }
+
+ let compiler = self.compiler;
+ let backend = self.backend;
+
+ let mut tarball =
+ Tarball::new(builder, &format!("rustc-codegen-{}", backend), &compiler.host.triple);
+ if backend == "cranelift" {
+ tarball.set_overlay(OverlayKind::RustcCodegenCranelift);
+ } else {
+ panic!("Unknown backend rustc_codegen_{}", backend);
+ }
+ tarball.is_preview(true);
+ tarball.add_legal_and_readme_to(format!("share/doc/rustc_codegen_{}", backend));
+
+ let src = builder.sysroot(compiler);
+ let backends_src = builder.sysroot_codegen_backends(compiler);
+ let backends_rel = backends_src
+ .strip_prefix(&src)
+ .unwrap()
+ .strip_prefix(builder.sysroot_libdir_relative(compiler))
+ .unwrap();
+ // Don't use custom libdir here because ^lib/ will be resolved again with installer
+ let backends_dst = PathBuf::from("lib").join(&backends_rel);
+
+ let backend_name = format!("rustc_codegen_{}", backend);
+ let mut found_backend = false;
+ for backend in fs::read_dir(&backends_src).unwrap() {
+ let file_name = backend.unwrap().file_name();
+ if file_name.to_str().unwrap().contains(&backend_name) {
+ tarball.add_file(backends_src.join(file_name), &backends_dst, 0o644);
+ found_backend = true;
+ }
+ }
+ assert!(found_backend);
+
+ Some(tarball.generate())
+ }
+}
+
+#[derive(Debug, PartialOrd, Ord, Copy, Clone, Hash, PartialEq, Eq)]
pub struct Rustfmt {
pub compiler: Compiler,
pub target: TargetSelection,
@@ -1447,6 +1535,10 @@ impl Step for Extended {
add_component!("clippy" => Clippy { compiler, target });
add_component!("miri" => Miri { compiler, target });
add_component!("analysis" => Analysis { compiler, target });
+ add_component!("rustc-codegen-cranelift" => CodegenBackend {
+ compiler: builder.compiler(stage, target),
+ backend: INTERNER.intern_str("cranelift"),
+ });
let etc = builder.src.join("src/etc/installer");
@@ -1538,7 +1630,7 @@ impl Step for Extended {
prepare("rust-analysis");
prepare("clippy");
prepare("rust-analyzer");
- for tool in &["rust-docs", "rust-demangler", "miri"] {
+ for tool in &["rust-docs", "rust-demangler", "miri", "rustc-codegen-cranelift"] {
if built_tools.contains(tool) {
prepare(tool);
}
@@ -1582,6 +1674,10 @@ impl Step for Extended {
"rust-demangler-preview".to_string()
} else if name == "miri" {
"miri-preview".to_string()
+ } else if name == "rustc-codegen-cranelift" {
+ // FIXME add installer support for cg_clif once it is ready to be distributed on
+ // windows.
+ unreachable!("cg_clif shouldn't be built for windows");
} else {
name.to_string()
};
@@ -2055,7 +2151,7 @@ impl Step for LlvmTools {
}
}
- builder.ensure(crate::llvm::Llvm { target });
+ builder.ensure(crate::core::build_steps::llvm::Llvm { target });
let mut tarball = Tarball::new(builder, "llvm-tools", &target.triple);
tarball.set_overlay(OverlayKind::LLVM);
@@ -2114,29 +2210,25 @@ impl Step for RustDev {
let mut tarball = Tarball::new(builder, "rust-dev", &target.triple);
tarball.set_overlay(OverlayKind::LLVM);
- builder.ensure(crate::llvm::Llvm { target });
+ builder.ensure(crate::core::build_steps::llvm::Llvm { target });
// We want to package `lld` to use it with `download-ci-llvm`.
- builder.ensure(crate::llvm::Lld { target });
+ builder.ensure(crate::core::build_steps::llvm::Lld { target });
let src_bindir = builder.llvm_out(target).join("bin");
- // If updating this list, you likely want to change
+ // If updating this, you likely want to change
// src/bootstrap/download-ci-llvm-stamp as well, otherwise local users
// will not pick up the extra file until LLVM gets bumped.
- for bin in &[
- "llvm-config",
- "llvm-ar",
- "llvm-objdump",
- "llvm-profdata",
- "llvm-bcanalyzer",
- "llvm-cov",
- "llvm-dwp",
- "llvm-nm",
- "llvm-dwarfdump",
- "llvm-dis",
- "llvm-tblgen",
- ] {
- tarball.add_file(src_bindir.join(exe(bin, target)), "bin", 0o755);
+ // We should include all the build artifacts obtained from a source build,
+ // so that you can use the downloadable LLVM as if you’ve just run a full source build.
+ if src_bindir.exists() {
+ for entry in walkdir::WalkDir::new(&src_bindir) {
+ let entry = t!(entry);
+ if entry.file_type().is_file() && !entry.path_is_symlink() {
+ let name = entry.file_name().to_str().unwrap();
+ tarball.add_file(src_bindir.join(name), "bin", 0o755);
+ }
+ }
}
// We don't build LLD on some platforms, so only add it if it exists
diff --git a/src/bootstrap/doc.rs b/src/bootstrap/src/core/build_steps/doc.rs
index 505f06ed1..e3fd942fc 100644
--- a/src/bootstrap/doc.rs
+++ b/src/bootstrap/src/core/build_steps/doc.rs
@@ -10,13 +10,13 @@
use std::fs;
use std::path::{Path, PathBuf};
-use crate::builder::crate_description;
-use crate::builder::{Alias, Builder, Compiler, Kind, RunConfig, ShouldRun, Step};
-use crate::cache::{Interned, INTERNER};
-use crate::compile;
-use crate::config::{Config, TargetSelection};
-use crate::tool::{self, prepare_tool_cargo, SourceType, Tool};
-use crate::util::{dir_is_empty, symlink_dir, t, up_to_date};
+use crate::core::build_steps::compile;
+use crate::core::build_steps::tool::{self, prepare_tool_cargo, SourceType, Tool};
+use crate::core::builder::crate_description;
+use crate::core::builder::{Alias, Builder, Compiler, Kind, RunConfig, ShouldRun, Step};
+use crate::core::config::{Config, TargetSelection};
+use crate::utils::cache::{Interned, INTERNER};
+use crate::utils::helpers::{dir_is_empty, symlink_dir, t, up_to_date};
use crate::Mode;
macro_rules! submodule_helper {
@@ -685,19 +685,6 @@ impl Step for Rustc {
target,
);
- // This uses a shared directory so that librustdoc documentation gets
- // correctly built and merged with the rustc documentation. This is
- // needed because rustdoc is built in a different directory from
- // rustc. rustdoc needs to be able to see everything, for example when
- // merging the search index, or generating local (relative) links.
- let out_dir = builder.stage_out(compiler, Mode::Rustc).join(target.triple).join("doc");
- t!(fs::create_dir_all(out_dir.parent().unwrap()));
- symlink_dir_force(&builder.config, &out, &out_dir);
- // Cargo puts proc macros in `target/doc` even if you pass `--target`
- // explicitly (https://github.com/rust-lang/cargo/issues/7677).
- let proc_macro_out_dir = builder.stage_out(compiler, Mode::Rustc).join("doc");
- symlink_dir_force(&builder.config, &out, &proc_macro_out_dir);
-
// Build cargo command.
let mut cargo = builder.cargo(compiler, Mode::Rustc, SourceType::InTree, target, "doc");
cargo.rustdocflag("--document-private-items");
@@ -724,6 +711,7 @@ impl Step for Rustc {
let mut to_open = None;
+ let out_dir = builder.stage_out(compiler, Mode::Rustc).join(target.triple).join("doc");
for krate in &*self.crates {
// Create all crate output directories first to make sure rustdoc uses
// relative links.
@@ -736,8 +724,29 @@ impl Step for Rustc {
}
}
+ // This uses a shared directory so that librustdoc documentation gets
+ // correctly built and merged with the rustc documentation.
+ //
+ // This is needed because rustdoc is built in a different directory from
+ // rustc. rustdoc needs to be able to see everything, for example when
+ // merging the search index, or generating local (relative) links.
+ symlink_dir_force(&builder.config, &out, &out_dir);
+ // Cargo puts proc macros in `target/doc` even if you pass `--target`
+ // explicitly (https://github.com/rust-lang/cargo/issues/7677).
+ let proc_macro_out_dir = builder.stage_out(compiler, Mode::Rustc).join("doc");
+ symlink_dir_force(&builder.config, &out, &proc_macro_out_dir);
+
builder.run(&mut cargo.into());
+ if !builder.config.dry_run() {
+ // Sanity check on linked compiler crates
+ for krate in &*self.crates {
+ let dir_name = krate.replace("-", "_");
+ // Making sure the directory exists and is not empty.
+ assert!(out.join(&*dir_name).read_dir().unwrap().next().is_some());
+ }
+ }
+
if builder.paths.iter().any(|path| path.ends_with("compiler")) {
// For `x.py doc compiler --open`, open `rustc_middle` by default.
let index = out.join("rustc_middle").join("index.html");
@@ -756,10 +765,10 @@ macro_rules! tool_doc {
$should_run: literal,
$path: literal,
$(rustc_tool = $rustc_tool:literal, )?
- $(in_tree = $in_tree:literal, )?
- [$($extra_arg: literal),+ $(,)?]
- $(,)?
- ) => {
+ $(in_tree = $in_tree:literal ,)?
+ $(is_library = $is_library:expr,)?
+ $(crates = $crates:expr)?
+ ) => {
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
pub struct $tool {
target: TargetSelection,
@@ -812,17 +821,6 @@ macro_rules! tool_doc {
SourceType::Submodule
};
- // Symlink compiler docs to the output directory of rustdoc documentation.
- let out_dirs = [
- builder.stage_out(compiler, Mode::ToolRustc).join(target.triple).join("doc"),
- // Cargo uses a different directory for proc macros.
- builder.stage_out(compiler, Mode::ToolRustc).join("doc"),
- ];
- for out_dir in out_dirs {
- t!(fs::create_dir_all(&out_dir));
- symlink_dir_force(&builder.config, &out, &out_dir);
- }
-
// Build cargo command.
let mut cargo = prepare_tool_cargo(
builder,
@@ -839,9 +837,13 @@ macro_rules! tool_doc {
// Only include compiler crates, no dependencies of those, such as `libc`.
cargo.arg("--no-deps");
- $(
- cargo.arg($extra_arg);
- )+
+ if false $(|| $is_library)? {
+ cargo.arg("--lib");
+ }
+
+ $(for krate in $crates {
+ cargo.arg("-p").arg(krate);
+ })?
cargo.rustdocflag("--document-private-items");
// Since we always pass --document-private-items, there's no need to warn about linking to private items.
@@ -851,62 +853,69 @@ macro_rules! tool_doc {
cargo.rustdocflag("--generate-link-to-definition");
cargo.rustdocflag("-Zunstable-options");
+ let out_dir = builder.stage_out(compiler, Mode::ToolRustc).join(target.triple).join("doc");
+ $(for krate in $crates {
+ let dir_name = krate.replace("-", "_");
+ t!(fs::create_dir_all(out_dir.join(&*dir_name)));
+ })?
+
+ // Symlink compiler docs to the output directory of rustdoc documentation.
+ symlink_dir_force(&builder.config, &out, &out_dir);
+ let proc_macro_out_dir = builder.stage_out(compiler, Mode::ToolRustc).join("doc");
+ symlink_dir_force(&builder.config, &out, &proc_macro_out_dir);
+
let _guard = builder.msg_doc(compiler, stringify!($tool).to_lowercase(), target);
builder.run(&mut cargo.into());
+
+ if !builder.config.dry_run() {
+ // Sanity check on linked doc directories
+ $(for krate in $crates {
+ let dir_name = krate.replace("-", "_");
+ // Making sure the directory exists and is not empty.
+ assert!(out.join(&*dir_name).read_dir().unwrap().next().is_some());
+ })?
+ }
}
}
}
}
-tool_doc!(
- Rustdoc,
- "rustdoc-tool",
- "src/tools/rustdoc",
- ["-p", "rustdoc", "-p", "rustdoc-json-types"]
-);
+tool_doc!(Rustdoc, "rustdoc-tool", "src/tools/rustdoc", crates = ["rustdoc", "rustdoc-json-types"]);
tool_doc!(
Rustfmt,
"rustfmt-nightly",
"src/tools/rustfmt",
- ["-p", "rustfmt-nightly", "-p", "rustfmt-config_proc_macro"],
+ crates = ["rustfmt-nightly", "rustfmt-config_proc_macro"]
);
-tool_doc!(Clippy, "clippy", "src/tools/clippy", ["-p", "clippy_utils"]);
-tool_doc!(Miri, "miri", "src/tools/miri", ["-p", "miri"]);
+tool_doc!(Clippy, "clippy", "src/tools/clippy", crates = ["clippy_config", "clippy_utils"]);
+tool_doc!(Miri, "miri", "src/tools/miri", crates = ["miri"]);
tool_doc!(
Cargo,
"cargo",
"src/tools/cargo",
rustc_tool = false,
in_tree = false,
- [
- "-p",
+ crates = [
"cargo",
- "-p",
"cargo-platform",
- "-p",
"cargo-util",
- "-p",
"crates-io",
- "-p",
"cargo-test-macro",
- "-p",
"cargo-test-support",
- "-p",
"cargo-credential",
- "-p",
"mdman",
// FIXME: this trips a license check in tidy.
- // "-p",
// "resolver-tests",
]
);
-tool_doc!(Tidy, "tidy", "src/tools/tidy", rustc_tool = false, ["-p", "tidy"]);
+tool_doc!(Tidy, "tidy", "src/tools/tidy", rustc_tool = false, crates = ["tidy"]);
tool_doc!(
Bootstrap,
"bootstrap",
"src/bootstrap",
rustc_tool = false,
- ["--lib", "-p", "bootstrap"]
+ is_library = true,
+ crates = ["bootstrap"]
);
#[derive(Ord, PartialOrd, Debug, Copy, Clone, Hash, PartialEq, Eq)]
diff --git a/src/bootstrap/format.rs b/src/bootstrap/src/core/build_steps/format.rs
index 11f2762f7..86f1d925f 100644
--- a/src/bootstrap/format.rs
+++ b/src/bootstrap/src/core/build_steps/format.rs
@@ -1,7 +1,7 @@
//! Runs rustfmt on the repository.
-use crate::builder::Builder;
-use crate::util::{output, program_out_of_date, t};
+use crate::core::builder::Builder;
+use crate::utils::helpers::{output, program_out_of_date, t};
use build_helper::ci::CiEnv;
use build_helper::git::get_git_modified_files;
use ignore::WalkBuilder;
@@ -89,7 +89,7 @@ fn get_modified_rs_files(build: &Builder<'_>) -> Result<Option<Vec<String>>, Str
return Ok(None);
}
- get_git_modified_files(Some(&build.config.src), &vec!["rs"])
+ get_git_modified_files(&build.config.git_config(), Some(&build.config.src), &vec!["rs"])
}
#[derive(serde_derive::Deserialize)]
diff --git a/src/bootstrap/install.rs b/src/bootstrap/src/core/build_steps/install.rs
index 500b20b86..6b4a8f597 100644
--- a/src/bootstrap/install.rs
+++ b/src/bootstrap/src/core/build_steps/install.rs
@@ -8,15 +8,14 @@ use std::fs;
use std::path::{Component, Path, PathBuf};
use std::process::Command;
-use crate::util::t;
-
-use crate::dist;
-use crate::tarball::GeneratedTarball;
+use crate::core::build_steps::dist;
+use crate::core::builder::{Builder, RunConfig, ShouldRun, Step};
+use crate::core::config::{Config, TargetSelection};
+use crate::utils::helpers::t;
+use crate::utils::tarball::GeneratedTarball;
+use crate::INTERNER;
use crate::{Compiler, Kind};
-use crate::builder::{Builder, RunConfig, ShouldRun, Step};
-use crate::config::{Config, TargetSelection};
-
#[cfg(target_os = "illumos")]
const SHELL: &str = "bash";
#[cfg(not(target_os = "illumos"))]
@@ -270,7 +269,7 @@ install!((self, builder, _config),
}
};
RustDemangler, alias = "rust-demangler", Self::should_build(_config), only_hosts: true, {
- // Note: Even though `should_build` may return true for `extended` default tools,
+ // NOTE: Even though `should_build` may return true for `extended` default tools,
// dist::RustDemangler may still return None, unless the target-dependent `profiler` config
// is also true, or the `tools` array explicitly includes "rust-demangler".
if let Some(tarball) = builder.ensure(dist::RustDemangler {
@@ -291,6 +290,19 @@ install!((self, builder, _config),
});
install_sh(builder, "rustc", self.compiler.stage, Some(self.target), &tarball);
};
+ RustcCodegenCranelift, alias = "rustc-codegen-cranelift", Self::should_build(_config), only_hosts: true, {
+ if let Some(tarball) = builder.ensure(dist::CodegenBackend {
+ compiler: self.compiler,
+ backend: INTERNER.intern_str("cranelift"),
+ }) {
+ install_sh(builder, "rustc-codegen-cranelift", self.compiler.stage, Some(self.target), &tarball);
+ } else {
+ builder.info(
+ &format!("skipping Install CodegenBackend(\"cranelift\") stage{} ({})",
+ self.compiler.stage, self.target),
+ );
+ }
+ };
);
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
diff --git a/src/bootstrap/llvm.rs b/src/bootstrap/src/core/build_steps/llvm.rs
index 455683158..a1f6fac8a 100644
--- a/src/bootstrap/llvm.rs
+++ b/src/bootstrap/src/core/build_steps/llvm.rs
@@ -16,11 +16,10 @@ use std::io;
use std::path::{Path, PathBuf};
use std::process::Command;
-use crate::builder::{Builder, RunConfig, ShouldRun, Step};
-use crate::channel;
-use crate::config::{Config, TargetSelection};
-use crate::util::get_clang_cl_resource_dir;
-use crate::util::{self, exe, output, t, up_to_date};
+use crate::core::builder::{Builder, RunConfig, ShouldRun, Step};
+use crate::core::config::{Config, TargetSelection};
+use crate::utils::channel;
+use crate::utils::helpers::{self, exe, get_clang_cl_resource_dir, output, t, up_to_date};
use crate::{CLang, GitRepo, Kind};
use build_helper::ci::CiEnv;
@@ -133,8 +132,8 @@ pub(crate) fn detect_llvm_sha(config: &Config, is_git: bool) -> String {
// walk back further to the last bors merge commit that actually changed LLVM. The first
// step will fail on CI because only the `auto` branch exists; we just fall back to `HEAD`
// in that case.
- let closest_upstream =
- get_git_merge_base(Some(&config.src)).unwrap_or_else(|_| "HEAD".into());
+ let closest_upstream = get_git_merge_base(&config.git_config(), Some(&config.src))
+ .unwrap_or_else(|_| "HEAD".into());
let mut rev_list = config.git();
rev_list.args(&[
PathBuf::from("rev-list"),
@@ -157,9 +156,9 @@ pub(crate) fn detect_llvm_sha(config: &Config, is_git: bool) -> String {
if llvm_sha.is_empty() {
eprintln!("error: could not find commit hash for downloading LLVM");
- eprintln!("help: maybe your repository history is too shallow?");
- eprintln!("help: consider disabling `download-ci-llvm`");
- eprintln!("help: or fetch enough history to include one upstream commit");
+ eprintln!("HELP: maybe your repository history is too shallow?");
+ eprintln!("HELP: consider disabling `download-ci-llvm`");
+ eprintln!("HELP: or fetch enough history to include one upstream commit");
panic!();
}
@@ -281,7 +280,7 @@ impl Step for Llvm {
let _guard = builder.msg_unstaged(Kind::Build, "LLVM", target);
t!(stamp.remove());
- let _time = util::timeit(&builder);
+ let _time = helpers::timeit(&builder);
t!(fs::create_dir_all(&out_dir));
// https://llvm.org/docs/CMake.html
@@ -397,6 +396,12 @@ impl Step for Llvm {
ldflags.shared.push(" -latomic");
}
+ if target.starts_with("mips") && target.contains("netbsd") {
+ // LLVM wants 64-bit atomics, while mipsel is 32-bit only, so needs -latomic
+ ldflags.exe.push(" -latomic");
+ ldflags.shared.push(" -latomic");
+ }
+
if target.contains("msvc") {
cfg.define("LLVM_USE_CRT_DEBUG", "MT");
cfg.define("LLVM_USE_CRT_RELEASE", "MT");
@@ -410,7 +415,7 @@ impl Step for Llvm {
let mut enabled_llvm_projects = Vec::new();
- if util::forcing_clang_based_tests() {
+ if helpers::forcing_clang_based_tests() {
enabled_llvm_projects.push("clang");
enabled_llvm_projects.push("compiler-rt");
}
@@ -528,8 +533,12 @@ impl Step for Llvm {
// If the shared library exists in LLVM's `/build/lib/` or `/lib/` folders, strip its
// debuginfo.
- crate::compile::strip_debug(builder, target, &out_dir.join("lib").join(&lib_name));
- crate::compile::strip_debug(
+ crate::core::build_steps::compile::strip_debug(
+ builder,
+ target,
+ &out_dir.join("lib").join(&lib_name),
+ );
+ crate::core::build_steps::compile::strip_debug(
builder,
target,
&out_dir.join("build").join("lib").join(&lib_name),
@@ -846,7 +855,7 @@ impl Step for Lld {
}
let _guard = builder.msg_unstaged(Kind::Build, "LLD", target);
- let _time = util::timeit(&builder);
+ let _time = helpers::timeit(&builder);
t!(fs::create_dir_all(&out_dir));
let mut cfg = cmake::Config::new(builder.src.join("src/llvm-project/lld"));
@@ -877,7 +886,7 @@ impl Step for Lld {
// `LD_LIBRARY_PATH` overrides)
//
if builder.config.rpath_enabled(target)
- && util::use_host_linker(target)
+ && helpers::use_host_linker(target)
&& builder.config.llvm_link_shared()
&& target.contains("linux")
{
@@ -970,7 +979,7 @@ impl Step for Sanitizers {
let _guard = builder.msg_unstaged(Kind::Build, "sanitizers", self.target);
t!(stamp.remove());
- let _time = util::timeit(&builder);
+ let _time = helpers::timeit(&builder);
let mut cfg = cmake::Config::new(&compiler_rt_dir);
cfg.profile("Release");
diff --git a/src/bootstrap/src/core/build_steps/mod.rs b/src/bootstrap/src/core/build_steps/mod.rs
new file mode 100644
index 000000000..50d83789b
--- /dev/null
+++ b/src/bootstrap/src/core/build_steps/mod.rs
@@ -0,0 +1,15 @@
+pub(crate) mod check;
+pub(crate) mod clean;
+pub(crate) mod compile;
+pub(crate) mod dist;
+pub(crate) mod doc;
+pub(crate) mod format;
+pub(crate) mod install;
+pub(crate) mod llvm;
+pub(crate) mod run;
+pub(crate) mod setup;
+pub(crate) mod suggest;
+pub(crate) mod synthetic_targets;
+pub(crate) mod test;
+pub(crate) mod tool;
+pub(crate) mod toolstate;
diff --git a/src/bootstrap/run.rs b/src/bootstrap/src/core/build_steps/run.rs
index 4082f5bb9..d1d6b7e86 100644
--- a/src/bootstrap/run.rs
+++ b/src/bootstrap/src/core/build_steps/run.rs
@@ -1,15 +1,13 @@
use std::path::PathBuf;
use std::process::Command;
-use clap_complete::shells;
-
-use crate::builder::{Builder, RunConfig, ShouldRun, Step};
-use crate::config::TargetSelection;
-use crate::dist::distdir;
-use crate::flags::get_completion;
-use crate::test;
-use crate::tool::{self, SourceType, Tool};
-use crate::util::output;
+use crate::core::build_steps::dist::distdir;
+use crate::core::build_steps::test;
+use crate::core::build_steps::tool::{self, SourceType, Tool};
+use crate::core::builder::{Builder, RunConfig, ShouldRun, Step};
+use crate::core::config::flags::get_completion;
+use crate::core::config::TargetSelection;
+use crate::utils::helpers::output;
use crate::Mode;
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
@@ -268,23 +266,29 @@ impl Step for GenerateWindowsSys {
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct GenerateCompletions;
+macro_rules! generate_completions {
+ ( $( ( $shell:ident, $filename:expr ) ),* ) => {
+ $(
+ if let Some(comp) = get_completion($shell, &$filename) {
+ std::fs::write(&$filename, comp).expect(&format!("writing {} completion", stringify!($shell)));
+ }
+ )*
+ };
+}
+
impl Step for GenerateCompletions {
type Output = ();
/// Uses `clap_complete` to generate shell completions.
fn run(self, builder: &Builder<'_>) {
- // FIXME(clubby789): enable zsh when clap#4898 is fixed
- let [bash, fish, powershell] = ["x.py.sh", "x.py.fish", "x.py.ps1"]
- .map(|filename| builder.src.join("src/etc/completions").join(filename));
- if let Some(comp) = get_completion(shells::Bash, &bash) {
- std::fs::write(&bash, comp).expect("writing bash completion");
- }
- if let Some(comp) = get_completion(shells::Fish, &fish) {
- std::fs::write(&fish, comp).expect("writing fish completion");
- }
- if let Some(comp) = get_completion(shells::PowerShell, &powershell) {
- std::fs::write(&powershell, comp).expect("writing powershell completion");
- }
+ use clap_complete::shells::{Bash, Fish, PowerShell, Zsh};
+
+ generate_completions!(
+ (Bash, builder.src.join("src/etc/completions/x.py.sh")),
+ (Zsh, builder.src.join("src/etc/completions/x.py.zsh")),
+ (Fish, builder.src.join("src/etc/completions/x.py.fish")),
+ (PowerShell, builder.src.join("src/etc/completions/x.py.ps1"))
+ );
}
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
diff --git a/src/bootstrap/setup.rs b/src/bootstrap/src/core/build_steps/setup.rs
index ef0234957..486a1e20f 100644
--- a/src/bootstrap/setup.rs
+++ b/src/bootstrap/src/core/build_steps/setup.rs
@@ -1,6 +1,6 @@
-use crate::builder::{Builder, RunConfig, ShouldRun, Step};
+use crate::core::builder::{Builder, RunConfig, ShouldRun, Step};
use crate::Config;
-use crate::{t, VERSION};
+use crate::{t, CONFIG_CHANGE_HISTORY};
use sha2::Digest;
use std::env::consts::EXE_SUFFIX;
use std::fmt::Write as _;
@@ -12,6 +12,7 @@ use std::str::FromStr;
use std::{fmt, fs, io};
#[cfg(test)]
+#[path = "../../tests/setup.rs"]
mod tests;
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
@@ -35,7 +36,7 @@ static SETTINGS_HASHES: &[&str] = &[
"47d227f424bf889b0d899b9cc992d5695e1b78c406e183cd78eafefbe5488923",
"b526bd58d0262dd4dda2bff5bc5515b705fb668a46235ace3e057f807963a11a",
];
-static RUST_ANALYZER_SETTINGS: &str = include_str!("../etc/rust_analyzer_settings.json");
+static RUST_ANALYZER_SETTINGS: &str = include_str!("../../../../etc/rust_analyzer_settings.json");
impl Profile {
fn include_path(&self, src_path: &Path) -> PathBuf {
@@ -121,6 +122,27 @@ impl Step for Profile {
return;
}
+ let path = &run.builder.config.config.clone().unwrap_or(PathBuf::from("config.toml"));
+ if path.exists() {
+ eprintln!();
+ eprintln!(
+ "ERROR: you asked for a new config file, but one already exists at `{}`",
+ t!(path.canonicalize()).display()
+ );
+
+ match prompt_user(
+ "Do you wish to override the existing configuration (which will allow the setup process to continue)?: [y/N]",
+ ) {
+ Ok(Some(PromptResult::Yes)) => {
+ t!(fs::remove_file(path));
+ }
+ _ => {
+ println!("Exiting.");
+ crate::exit!(1);
+ }
+ }
+ }
+
// for Profile, `run.paths` will have 1 and only 1 element
// this is because we only accept at most 1 path from user input.
// If user calls `x.py setup` without arguments, the interactive TUI
@@ -146,6 +168,15 @@ impl Step for Profile {
}
fn run(self, builder: &Builder<'_>) {
+ // During ./x.py setup once you select the codegen profile.
+ // The submodule will be downloaded. It does not work in the
+ // tarball case since they don't include Git and submodules
+ // are already included.
+ if !builder.rust_info().is_from_tarball() {
+ if self == Profile::Codegen {
+ builder.update_submodule(&Path::new("src/llvm-project"));
+ }
+ }
setup(&builder.build.config, self)
}
}
@@ -181,8 +212,8 @@ pub fn setup(config: &Config, profile: Profile) {
if profile == Profile::Tools {
eprintln!();
eprintln!(
- "note: the `tools` profile sets up the `stage2` toolchain (use \
- `rustup toolchain link 'name' host/build/stage2` to use rustc)"
+ "NOTE: the `tools` profile sets up the `stage2` toolchain (use \
+ `rustup toolchain link 'name' build/host/stage2` to use rustc)"
)
}
@@ -194,24 +225,12 @@ fn setup_config_toml(path: &PathBuf, profile: Profile, config: &Config) {
if profile == Profile::None {
return;
}
- if path.exists() {
- eprintln!();
- eprintln!(
- "error: you asked `x.py` to setup a new config file, but one already exists at `{}`",
- path.display()
- );
- eprintln!("help: try adding `profile = \"{}\"` at the top of {}", profile, path.display());
- eprintln!(
- "note: this will use the configuration in {}",
- profile.include_path(&config.src).display()
- );
- crate::exit!(1);
- }
+ let latest_change_id = CONFIG_CHANGE_HISTORY.last().unwrap();
let settings = format!(
"# Includes one of the default files in src/bootstrap/defaults\n\
profile = \"{profile}\"\n\
- changelog-seen = {VERSION}\n"
+ change-id = {latest_change_id}\n"
);
t!(fs::write(path, settings));
@@ -395,8 +414,8 @@ pub fn interactive_path() -> io::Result<Profile> {
break match parse_with_abbrev(&input) {
Ok(profile) => profile,
Err(err) => {
- eprintln!("error: {err}");
- eprintln!("note: press Ctrl+C to exit");
+ eprintln!("ERROR: {err}");
+ eprintln!("NOTE: press Ctrl+C to exit");
continue;
}
};
@@ -425,8 +444,8 @@ fn prompt_user(prompt: &str) -> io::Result<Option<PromptResult>> {
"p" | "print" => return Ok(Some(PromptResult::Print)),
"" => return Ok(None),
_ => {
- eprintln!("error: unrecognized option '{}'", input.trim());
- eprintln!("note: press Ctrl+C to exit");
+ eprintln!("ERROR: unrecognized option '{}'", input.trim());
+ eprintln!("NOTE: press Ctrl+C to exit");
}
};
}
@@ -467,7 +486,8 @@ fn install_git_hook_maybe(config: &Config) -> io::Result<()> {
assert!(output.status.success(), "failed to run `git`");
PathBuf::from(t!(String::from_utf8(output.stdout)).trim())
}));
- let dst = git.join("hooks").join("pre-push");
+ let hooks_dir = git.join("hooks");
+ let dst = hooks_dir.join("pre-push");
if dst.exists() {
// The git hook has already been set up, or the user already has a custom hook.
return Ok(());
@@ -484,11 +504,15 @@ undesirable, simply delete the `pre-push` file from .git/hooks."
println!("Ok, skipping installation!");
return Ok(());
}
+ if !hooks_dir.exists() {
+ // We need to (try to) create the hooks directory first.
+ let _ = fs::create_dir(hooks_dir);
+ }
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{}",
+ "ERROR: could not create hook {}: do you already have the git hook installed?\n{}",
dst.display(),
e
);
@@ -554,10 +578,10 @@ fn create_vscode_settings_maybe(config: &Config) -> io::Result<()> {
);
match mismatched_settings {
Some(true) => eprintln!(
- "warning: existing `.vscode/settings.json` is out of date, x.py will update it"
+ "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"
+ "WARNING: existing `.vscode/settings.json` has been modified by user, x.py will back it up and replace it"
),
_ => (),
}
@@ -584,7 +608,7 @@ fn create_vscode_settings_maybe(config: &Config) -> io::Result<()> {
// exists and is not current version or outdated, so back it up
let mut backup = vscode_settings.clone();
backup.set_extension("json.bak");
- eprintln!("warning: copying `settings.json` to `settings.json.bak`");
+ eprintln!("WARNING: copying `settings.json` to `settings.json.bak`");
fs::copy(&vscode_settings, &backup)?;
"Updated"
}
diff --git a/src/bootstrap/suggest.rs b/src/bootstrap/src/core/build_steps/suggest.rs
index f225104bd..93da27560 100644
--- a/src/bootstrap/suggest.rs
+++ b/src/bootstrap/src/core/build_steps/suggest.rs
@@ -1,17 +1,21 @@
#![cfg_attr(feature = "build-metrics", allow(unused))]
-use std::str::FromStr;
-
-use std::path::PathBuf;
-
use clap::Parser;
+use std::path::PathBuf;
+use std::str::FromStr;
-use crate::{builder::Builder, tool::Tool};
+use crate::core::build_steps::tool::Tool;
+use crate::core::builder::Builder;
/// Suggests a list of possible `x.py` commands to run based on modified files in branch.
pub fn suggest(builder: &Builder<'_>, run: bool) {
- let suggestions =
- builder.tool_cmd(Tool::SuggestTests).output().expect("failed to run `suggest-tests` tool");
+ let git_config = builder.config.git_config();
+ let suggestions = builder
+ .tool_cmd(Tool::SuggestTests)
+ .env("SUGGEST_TESTS_GIT_REPOSITORY", git_config.git_repository)
+ .env("SUGGEST_TESTS_NIGHTLY_BRANCH", git_config.nightly_branch)
+ .output()
+ .expect("failed to run `suggest-tests` tool");
if !suggestions.status.success() {
println!("failed to run `suggest-tests` tool ({})", suggestions.status);
@@ -62,13 +66,13 @@ pub fn suggest(builder: &Builder<'_>, run: bool) {
for sug in suggestions {
let mut build: crate::Build = builder.build.clone();
build.config.paths = sug.2;
- build.config.cmd = crate::flags::Flags::parse_from(["x.py", sug.0]).cmd;
+ build.config.cmd = crate::core::config::flags::Flags::parse_from(["x.py", sug.0]).cmd;
if let Some(stage) = sug.1 {
build.config.stage = stage;
}
build.build();
}
} else {
- println!("help: consider using the `--run` flag to automatically run suggested tests");
+ println!("HELP: consider using the `--run` flag to automatically run suggested tests");
}
}
diff --git a/src/bootstrap/synthetic_targets.rs b/src/bootstrap/src/core/build_steps/synthetic_targets.rs
index 7eeac9025..d2c65b740 100644
--- a/src/bootstrap/synthetic_targets.rs
+++ b/src/bootstrap/src/core/build_steps/synthetic_targets.rs
@@ -7,8 +7,8 @@
//! one of the target specs already defined in this module, or create new ones by adding a new step
//! that calls create_synthetic_target.
-use crate::builder::{Builder, ShouldRun, Step};
-use crate::config::TargetSelection;
+use crate::core::builder::{Builder, ShouldRun, Step};
+use crate::core::config::TargetSelection;
use crate::Compiler;
use std::process::{Command, Stdio};
@@ -76,7 +76,7 @@ fn create_synthetic_target(
std::fs::write(&path, &serde_json::to_vec_pretty(&spec).unwrap()).unwrap();
let target = TargetSelection::create_synthetic(&name, path.to_str().unwrap());
- crate::cc_detect::find_target(builder, target);
+ crate::utils::cc_detect::find_target(builder, target);
target
}
diff --git a/src/bootstrap/test.rs b/src/bootstrap/src/core/build_steps/test.rs
index ba030f0f5..d2aa89dee 100644
--- a/src/bootstrap/test.rs
+++ b/src/bootstrap/src/core/build_steps/test.rs
@@ -13,21 +13,26 @@ use std::process::{Command, Stdio};
use clap_complete::shells;
-use crate::builder::crate_description;
-use crate::builder::{Builder, Compiler, Kind, RunConfig, ShouldRun, Step};
-use crate::cache::Interned;
-use crate::cache::INTERNER;
-use crate::compile;
-use crate::config::TargetSelection;
-use crate::dist;
-use crate::doc::DocumentationFormat;
-use crate::flags::Subcommand;
-use crate::llvm;
-use crate::render_tests::add_flags_and_try_run_tests;
-use crate::synthetic_targets::MirOptPanicAbortSyntheticTarget;
-use crate::tool::{self, SourceType, Tool};
-use crate::toolstate::ToolState;
-use crate::util::{self, add_link_lib_path, dylib_path, dylib_path_var, output, t, up_to_date};
+use crate::core::build_steps::compile;
+use crate::core::build_steps::dist;
+use crate::core::build_steps::doc::DocumentationFormat;
+use crate::core::build_steps::llvm;
+use crate::core::build_steps::synthetic_targets::MirOptPanicAbortSyntheticTarget;
+use crate::core::build_steps::tool::{self, SourceType, Tool};
+use crate::core::build_steps::toolstate::ToolState;
+use crate::core::builder::crate_description;
+use crate::core::builder::{Builder, Compiler, Kind, RunConfig, ShouldRun, Step};
+use crate::core::config::flags::get_completion;
+use crate::core::config::flags::Subcommand;
+use crate::core::config::TargetSelection;
+use crate::utils;
+use crate::utils::cache::{Interned, INTERNER};
+use crate::utils::exec::BootstrapCommand;
+use crate::utils::helpers::{
+ self, add_link_lib_path, dylib_path, dylib_path_var, output, t,
+ target_supports_cranelift_backend, up_to_date,
+};
+use crate::utils::render_tests::{add_flags_and_try_run_tests, try_run_tests};
use crate::{envify, CLang, DocTests, GitRepo, Mode};
const ADB_TEST_DIR: &str = "/data/local/tmp/work";
@@ -167,7 +172,7 @@ You can skip linkcheck with --skip src/tools/linkchecker"
// Run the linkchecker.
let _guard =
builder.msg(Kind::Test, compiler.stage, "Linkcheck", bootstrap_host, bootstrap_host);
- let _time = util::timeit(&builder);
+ let _time = helpers::timeit(&builder);
builder.run_delaying_failure(linkchecker.arg(builder.out.join(host.triple).join("doc")));
}
@@ -219,7 +224,11 @@ impl Step for HtmlCheck {
}
// Ensure that a few different kinds of documentation are available.
builder.default_doc(&[]);
- builder.ensure(crate::doc::Rustc::new(builder.top_stage, self.target, builder));
+ builder.ensure(crate::core::build_steps::doc::Rustc::new(
+ builder.top_stage,
+ self.target,
+ builder,
+ ));
builder.run_delaying_failure(
builder.tool_cmd(Tool::HtmlChecker).arg(builder.doc_out(self.target)),
@@ -260,7 +269,7 @@ impl Step for Cargotest {
let out_dir = builder.out.join("ct");
t!(fs::create_dir_all(&out_dir));
- let _time = util::timeit(&builder);
+ let _time = helpers::timeit(&builder);
let mut cmd = builder.tool_cmd(Tool::CargoTest);
builder.run_delaying_failure(
cmd.arg(&cargo)
@@ -328,7 +337,7 @@ impl Step for Cargo {
builder,
);
- let _time = util::timeit(&builder);
+ let _time = helpers::timeit(&builder);
add_flags_and_try_run_tests(builder, &mut cargo);
}
}
@@ -622,7 +631,7 @@ impl Step for Miri {
SourceType::InTree,
&[],
);
- let _guard = builder.msg_sysroot_tool(Kind::Test, compiler.stage, "miri", host, host);
+ let _guard = builder.msg_sysroot_tool(Kind::Test, compiler.stage, "miri", host, target);
cargo.add_rustc_lib_path(builder, compiler);
@@ -642,7 +651,7 @@ impl Step for Miri {
// does not understand the flags added by `add_flags_and_try_run_test`.
let mut cargo = prepare_cargo_test(cargo, &[], &[], "miri", compiler, target, builder);
{
- let _time = util::timeit(&builder);
+ let _time = helpers::timeit(&builder);
builder.run(&mut cargo);
}
@@ -658,7 +667,7 @@ impl Step for Miri {
let mut cargo = prepare_cargo_test(cargo, &[], &[], "miri", compiler, target, builder);
{
- let _time = util::timeit(&builder);
+ let _time = helpers::timeit(&builder);
builder.run(&mut cargo);
}
}
@@ -698,7 +707,7 @@ impl Step for Miri {
let mut cargo = Command::from(cargo);
{
- let _time = util::timeit(&builder);
+ let _time = helpers::timeit(&builder);
builder.run(&mut cargo);
}
}
@@ -801,8 +810,8 @@ impl Step for Clippy {
let _guard = builder.msg_sysroot_tool(Kind::Test, compiler.stage, "clippy", host, host);
- #[allow(deprecated)] // Clippy reports errors if it blessed the outputs
- if builder.config.try_run(&mut cargo).is_ok() {
+ // Clippy reports errors if it blessed the outputs
+ if builder.run_cmd(BootstrapCommand::from(&mut cargo).allow_failure()) {
// The tests succeeded; nothing to do.
return;
}
@@ -859,7 +868,7 @@ impl Step for RustdocTheme {
if builder.is_fuse_ld_lld(self.compiler.host) {
cmd.env(
"RUSTDOC_LLD_NO_THREADS",
- util::lld_flag_no_threads(self.compiler.host.contains("windows")),
+ helpers::lld_flag_no_threads(self.compiler.host.contains("windows")),
);
}
builder.run_delaying_failure(&mut cmd);
@@ -900,7 +909,8 @@ impl Step for RustdocJSStd {
.arg("--test-folder")
.arg(builder.src.join("tests/rustdoc-js-std"));
for path in &builder.paths {
- if let Some(p) = util::is_valid_test_suite_arg(path, "tests/rustdoc-js-std", builder) {
+ if let Some(p) = helpers::is_valid_test_suite_arg(path, "tests/rustdoc-js-std", builder)
+ {
if !p.ends_with(".js") {
eprintln!("A non-js file was given: `{}`", path.display());
panic!("Cannot run rustdoc-js-std tests");
@@ -908,7 +918,7 @@ impl Step for RustdocJSStd {
command.arg("--test-file").arg(path);
}
}
- builder.ensure(crate::doc::Std::new(
+ builder.ensure(crate::core::build_steps::doc::Std::new(
builder.top_stage,
self.target,
builder,
@@ -1035,7 +1045,7 @@ impl Step for RustdocGUI {
.env("RUSTC", builder.rustc(self.compiler));
for path in &builder.paths {
- if let Some(p) = util::is_valid_test_suite_arg(path, "tests/rustdoc-gui", builder) {
+ if let Some(p) = helpers::is_valid_test_suite_arg(path, "tests/rustdoc-gui", builder) {
if !p.ends_with(".goml") {
eprintln!("A non-goml file was given: `{}`", path.display());
panic!("Cannot run rustdoc-gui tests");
@@ -1058,7 +1068,7 @@ impl Step for RustdocGUI {
cmd.arg("--npm").arg(npm);
}
- let _time = util::timeit(&builder);
+ let _time = helpers::timeit(&builder);
let _guard = builder.msg_sysroot_tool(
Kind::Test,
self.compiler.stage,
@@ -1066,7 +1076,7 @@ impl Step for RustdocGUI {
self.compiler.host,
self.target,
);
- crate::render_tests::try_run_tests(builder, &mut cmd, true);
+ try_run_tests(builder, &mut cmd, true);
}
}
@@ -1117,16 +1127,16 @@ impl Step for Tidy {
let inferred_rustfmt_dir = builder.initial_rustc.parent().unwrap();
eprintln!(
"\
-error: no `rustfmt` binary found in {PATH}
-info: `rust.channel` is currently set to \"{CHAN}\"
-help: if you are testing a beta branch, set `rust.channel` to \"beta\" in the `config.toml` file
-help: to skip test's attempt to check tidiness, pass `--skip src/tools/tidy` to `x.py test`",
+ERROR: no `rustfmt` binary found in {PATH}
+INFO: `rust.channel` is currently set to \"{CHAN}\"
+HELP: if you are testing a beta branch, set `rust.channel` to \"beta\" in the `config.toml` file
+HELP: to skip test's attempt to check tidiness, pass `--skip src/tools/tidy` to `x.py test`",
PATH = inferred_rustfmt_dir.display(),
CHAN = builder.config.channel,
);
crate::exit!(1);
}
- crate::format::format(&builder, !builder.config.cmd.bless(), &[]);
+ crate::core::build_steps::format::format(&builder, !builder.config.cmd.bless(), &[]);
}
builder.info("tidy check");
@@ -1135,13 +1145,14 @@ help: to skip test's attempt to check tidiness, pass `--skip src/tools/tidy` to
builder.ensure(ExpandYamlAnchors);
builder.info("x.py completions check");
- let [bash, fish, powershell] = ["x.py.sh", "x.py.fish", "x.py.ps1"]
+ let [bash, zsh, fish, powershell] = ["x.py.sh", "x.py.zsh", "x.py.fish", "x.py.ps1"]
.map(|filename| builder.src.join("src/etc/completions").join(filename));
if builder.config.cmd.bless() {
- builder.ensure(crate::run::GenerateCompletions);
- } else if crate::flags::get_completion(shells::Bash, &bash).is_some()
- || crate::flags::get_completion(shells::Fish, &fish).is_some()
- || crate::flags::get_completion(shells::PowerShell, &powershell).is_some()
+ builder.ensure(crate::core::build_steps::run::GenerateCompletions);
+ } else if get_completion(shells::Bash, &bash).is_some()
+ || get_completion(shells::Fish, &fish).is_some()
+ || get_completion(shells::PowerShell, &powershell).is_some()
+ || crate::flags::get_completion(shells::Zsh, &zsh).is_some()
{
eprintln!(
"x.py completions were changed; run `x.py run generate-completions` to update them"
@@ -1172,7 +1183,7 @@ impl Step for ExpandYamlAnchors {
/// appropriate configuration for all our CI providers. This step ensures the tool was called
/// by the user before committing CI changes.
fn run(self, builder: &Builder<'_>) {
- // Note: `.github/` is not included in dist-src tarballs
+ // NOTE: `.github/` is not included in dist-src tarballs
if !builder.src.join(".github/workflows/ci.yml").exists() {
builder.info("Skipping YAML anchors check: GitHub Actions config not found");
return;
@@ -1294,6 +1305,47 @@ macro_rules! test_definitions {
};
}
+/// Declares an alias for running the [`Coverage`] tests in only one mode.
+/// Adapted from [`test_definitions`].
+macro_rules! coverage_test_alias {
+ ($name:ident {
+ alias_and_mode: $alias_and_mode:expr,
+ default: $default:expr,
+ only_hosts: $only_hosts:expr $(,)?
+ }) => {
+ #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
+ pub struct $name {
+ pub compiler: Compiler,
+ pub target: TargetSelection,
+ }
+
+ impl $name {
+ const MODE: &'static str = $alias_and_mode;
+ }
+
+ impl Step for $name {
+ type Output = ();
+ const DEFAULT: bool = $default;
+ const ONLY_HOSTS: bool = $only_hosts;
+
+ fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
+ run.alias($alias_and_mode)
+ }
+
+ fn make_run(run: RunConfig<'_>) {
+ let compiler = run.builder.compiler(run.builder.top_stage, run.build_triple());
+
+ run.builder.ensure($name { compiler, target: run.target });
+ }
+
+ fn run(self, builder: &Builder<'_>) {
+ Coverage { compiler: self.compiler, target: self.target }
+ .run_unified_suite(builder, Self::MODE)
+ }
+ }
+ };
+}
+
default_test!(Ui { path: "tests/ui", mode: "ui", suite: "ui" });
default_test!(RunPassValgrind {
@@ -1338,17 +1390,70 @@ host_test!(RunMakeFullDeps {
default_test!(Assembly { path: "tests/assembly", mode: "assembly", suite: "assembly" });
-default_test!(CoverageMap {
- path: "tests/coverage-map",
- mode: "coverage-map",
- suite: "coverage-map"
+/// Custom test step that is responsible for running the coverage tests
+/// in multiple different modes.
+///
+/// Each individual mode also has its own alias that will run the tests in
+/// just that mode.
+#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
+pub struct Coverage {
+ pub compiler: Compiler,
+ pub target: TargetSelection,
+}
+
+impl Coverage {
+ const PATH: &'static str = "tests/coverage";
+ const SUITE: &'static str = "coverage";
+
+ fn run_unified_suite(&self, builder: &Builder<'_>, mode: &'static str) {
+ builder.ensure(Compiletest {
+ compiler: self.compiler,
+ target: self.target,
+ mode,
+ suite: Self::SUITE,
+ path: Self::PATH,
+ compare_mode: None,
+ })
+ }
+}
+
+impl Step for Coverage {
+ type Output = ();
+ const DEFAULT: bool = false;
+ const ONLY_HOSTS: bool = false;
+
+ fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
+ run.suite_path(Self::PATH)
+ }
+
+ fn make_run(run: RunConfig<'_>) {
+ let compiler = run.builder.compiler(run.builder.top_stage, run.build_triple());
+
+ run.builder.ensure(Coverage { compiler, target: run.target });
+ }
+
+ fn run(self, builder: &Builder<'_>) {
+ self.run_unified_suite(builder, CoverageMap::MODE);
+ self.run_unified_suite(builder, CoverageRun::MODE);
+ }
+}
+
+// Aliases for running the coverage tests in only one mode.
+coverage_test_alias!(CoverageMap {
+ alias_and_mode: "coverage-map",
+ default: true,
+ only_hosts: false,
+});
+coverage_test_alias!(CoverageRun {
+ alias_and_mode: "coverage-run",
+ default: true,
+ only_hosts: true,
});
-host_test!(RunCoverage { path: "tests/run-coverage", mode: "run-coverage", suite: "run-coverage" });
-host_test!(RunCoverageRustdoc {
- path: "tests/run-coverage-rustdoc",
- mode: "run-coverage",
- suite: "run-coverage-rustdoc"
+host_test!(CoverageRunRustdoc {
+ path: "tests/coverage-run-rustdoc",
+ mode: "coverage-run",
+ suite: "coverage-run-rustdoc"
});
// For the mir-opt suite we do not use macros, as we need custom behavior when blessing.
@@ -1402,10 +1507,10 @@ impl Step for MirOpt {
// have been detected by bootstrap if the target we're testing wasn't in the
// --target flags.
if !builder.cc.borrow().contains_key(&target_32bit) {
- crate::cc_detect::find_target(builder, target_32bit);
+ utils::cc_detect::find_target(builder, target_32bit);
}
if !builder.cc.borrow().contains_key(&target_64bit) {
- crate::cc_detect::find_target(builder, target_64bit);
+ utils::cc_detect::find_target(builder, target_64bit);
}
vec![target_32bit, target_64bit]
@@ -1461,10 +1566,10 @@ impl Step for Compiletest {
fn run(self, builder: &Builder<'_>) {
if builder.top_stage == 0 && env::var("COMPILETEST_FORCE_STAGE0").is_err() {
eprintln!("\
-error: `--stage 0` runs compiletest on the beta compiler, not your local changes, and will almost always cause tests to fail
-help: to test the compiler, use `--stage 1` instead
-help: to test the standard library, use `--stage 0 library/std` instead
-note: if you're sure you want to do this, please open an issue as to why. In the meantime, you can override this with `COMPILETEST_FORCE_STAGE0=1`."
+ERROR: `--stage 0` runs compiletest on the beta compiler, not your local changes, and will almost always cause tests to fail
+HELP: to test the compiler, use `--stage 1` instead
+HELP: to test the standard library, use `--stage 0 library/std` instead
+NOTE: if you're sure you want to do this, please open an issue as to why. In the meantime, you can override this with `COMPILETEST_FORCE_STAGE0=1`."
);
crate::exit!(1);
}
@@ -1535,7 +1640,7 @@ note: if you're sure you want to do this, please open an issue as to why. In the
|| (mode == "ui" && is_rustdoc)
|| mode == "js-doc-test"
|| mode == "rustdoc-json"
- || suite == "run-coverage-rustdoc"
+ || suite == "coverage-run-rustdoc"
{
cmd.arg("--rustdoc-path").arg(builder.rustdoc(compiler));
}
@@ -1557,10 +1662,12 @@ note: if you're sure you want to do this, please open an issue as to why. In the
cmd.arg("--coverage-dump-path").arg(coverage_dump);
}
- if mode == "run-make" || mode == "run-coverage" {
+ if mode == "coverage-run" {
+ // The demangler doesn't need the current compiler, so we can avoid
+ // unnecessary rebuilds by using the bootstrap compiler instead.
let rust_demangler = builder
.ensure(tool::RustDemangler {
- compiler,
+ compiler: compiler.with_stage(0),
target: compiler.host,
extra_features: Vec::new(),
})
@@ -1678,7 +1785,7 @@ note: if you're sure you want to do this, please open an issue as to why. In the
}
}
- if util::forcing_clang_based_tests() {
+ if helpers::forcing_clang_based_tests() {
let clang_exe = builder.llvm_out(target).join("bin").join("clang");
cmd.arg("--run-clang-based-tests-with").arg(clang_exe);
}
@@ -1697,7 +1804,7 @@ note: if you're sure you want to do this, please open an issue as to why. In the
// Get test-args by striping suite path
let mut test_args: Vec<&str> = paths
.iter()
- .filter_map(|p| util::is_valid_test_suite_arg(p, suite_path, builder))
+ .filter_map(|p| helpers::is_valid_test_suite_arg(p, suite_path, builder))
.collect();
test_args.append(&mut builder.config.test_args());
@@ -1747,11 +1854,11 @@ note: if you're sure you want to do this, please open an issue as to why. In the
}
if !builder.config.dry_run()
- && (matches!(suite, "run-make" | "run-make-fulldeps") || mode == "run-coverage")
+ && (matches!(suite, "run-make" | "run-make-fulldeps") || mode == "coverage-run")
{
// The llvm/bin directory contains many useful cross-platform
// tools. Pass the path to run-make tests so they can use them.
- // (The run-coverage tests also need these tools to process
+ // (The coverage-run tests also need these tools to process
// coverage reports.)
let llvm_bin_path = llvm_config
.parent()
@@ -1864,6 +1971,10 @@ note: if you're sure you want to do this, please open an issue as to why. In the
cmd.arg("--git-hash");
}
+ let git_config = builder.config.git_config();
+ cmd.arg("--git-repository").arg(git_config.git_repository);
+ cmd.arg("--nightly-branch").arg(git_config.nightly_branch);
+
builder.ci_env.force_coloring_in_ci(&mut cmd);
#[cfg(feature = "build-metrics")]
@@ -1886,7 +1997,7 @@ note: if you're sure you want to do this, please open an issue as to why. In the
compiler.host,
target,
);
- crate::render_tests::try_run_tests(builder, &mut cmd, false);
+ try_run_tests(builder, &mut cmd, false);
if let Some(compare_mode) = compare_mode {
cmd.arg("--compare-mode").arg(compare_mode);
@@ -1908,8 +2019,8 @@ note: if you're sure you want to do this, please open an issue as to why. In the
"Check compiletest suite={} mode={} compare_mode={} ({} -> {})",
suite, mode, compare_mode, &compiler.host, target
));
- let _time = util::timeit(&builder);
- crate::render_tests::try_run_tests(builder, &mut cmd, false);
+ let _time = helpers::timeit(&builder);
+ try_run_tests(builder, &mut cmd, false);
}
}
}
@@ -1980,7 +2091,7 @@ impl BookTest {
compiler.host,
compiler.host,
);
- let _time = util::timeit(&builder);
+ let _time = helpers::timeit(&builder);
let toolstate = if builder.run_delaying_failure(&mut rustbook_cmd) {
ToolState::TestPass
} else {
@@ -2002,7 +2113,7 @@ impl BookTest {
// Do a breadth-first traversal of the `src/doc` directory and just run
// tests for all files that end in `*.md`
let mut stack = vec![builder.src.join(self.path)];
- let _time = util::timeit(&builder);
+ let _time = helpers::timeit(&builder);
let mut files = Vec::new();
while let Some(p) = stack.pop() {
if p.is_dir() {
@@ -2113,7 +2224,7 @@ impl Step for ErrorIndex {
let guard =
builder.msg(Kind::Test, compiler.stage, "error-index", compiler.host, compiler.host);
- let _time = util::timeit(&builder);
+ let _time = helpers::timeit(&builder);
builder.run_quiet(&mut tool);
drop(guard);
// The tests themselves need to link to std, so make sure it is
@@ -2235,7 +2346,7 @@ fn run_cargo_test<'a>(
) -> bool {
let mut cargo =
prepare_cargo_test(cargo, libtest_args, crates, primary_crate, compiler, target, builder);
- let _time = util::timeit(&builder);
+ let _time = helpers::timeit(&builder);
let _group = description.into().and_then(|what| {
builder.msg_sysroot_tool(Kind::Test, compiler.stage, what, compiler.host, target)
});
@@ -2630,7 +2741,7 @@ impl Step for RemoteCopyLibs {
for f in t!(builder.sysroot_libdir(compiler, target).read_dir()) {
let f = t!(f);
let name = f.file_name().into_string().unwrap();
- if util::is_dylib(&name) {
+ if helpers::is_dylib(&name) {
builder.run(Command::new(&tool).arg("push").arg(f.path()));
}
}
@@ -2675,7 +2786,9 @@ impl Step for Distcheck {
.current_dir(&dir),
);
builder.run(
- Command::new(util::make(&builder.config.build.triple)).arg("check").current_dir(&dir),
+ Command::new(helpers::make(&builder.config.build.triple))
+ .arg("check")
+ .current_dir(&dir),
);
// Now make sure that rust-src has all of libstd's dependencies
@@ -2832,7 +2945,7 @@ impl Step for LintDocs {
/// Tests that the lint examples in the rustc book generate the correct
/// lints and have the expected format.
fn run(self, builder: &Builder<'_>) {
- builder.ensure(crate::doc::RustcBook {
+ builder.ensure(crate::core::build_steps::doc::RustcBook {
compiler: self.compiler,
target: self.target,
validate: true,
@@ -2940,10 +3053,6 @@ impl Step for TestHelpers {
let _guard = builder.msg_unstaged(Kind::Build, "test helpers", target);
t!(fs::create_dir_all(&dst));
let mut cfg = cc::Build::new();
- // FIXME: Workaround for https://github.com/emscripten-core/emscripten/issues/9013
- if target.contains("emscripten") {
- cfg.pic(false);
- }
// We may have found various cross-compilers a little differently due to our
// extra configuration, so inform cc of these compilers. Note, though, that
@@ -2990,15 +3099,7 @@ impl Step for CodegenCranelift {
return;
}
- let triple = run.target.triple;
- let target_supported = if triple.contains("linux") {
- triple.contains("x86_64") || triple.contains("aarch64") || triple.contains("s390x")
- } else if triple.contains("darwin") || triple.contains("windows") {
- triple.contains("x86_64")
- } else {
- false
- };
- if !target_supported {
+ if !target_supports_cranelift_backend(run.target) {
builder.info("target not supported by rustc_codegen_cranelift. skipping");
return;
}
@@ -3055,11 +3156,12 @@ impl Step for CodegenCranelift {
&compiler.host,
target
));
- let _time = util::timeit(&builder);
+ let _time = helpers::timeit(&builder);
// FIXME handle vendoring for source tarballs before removing the --skip-test below
let download_dir = builder.out.join("cg_clif_download");
+ // FIXME: Uncomment the `prepare` command below once vendoring is implemented.
/*
let mut prepare_cargo = build_cargo();
prepare_cargo.arg("--").arg("prepare").arg("--download-dir").arg(&download_dir);
@@ -3087,7 +3189,127 @@ impl Step for CodegenCranelift {
.arg("testsuite.extended_sysroot");
cargo.args(builder.config.test_args());
+ let mut cmd: Command = cargo.into();
+ builder.run_cmd(BootstrapCommand::from(&mut cmd).fail_fast());
+ }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct CodegenGCC {
+ compiler: Compiler,
+ target: TargetSelection,
+}
+
+impl Step for CodegenGCC {
+ type Output = ();
+ const DEFAULT: bool = true;
+ const ONLY_HOSTS: bool = true;
+
+ fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
+ run.paths(&["compiler/rustc_codegen_gcc"])
+ }
+
+ fn make_run(run: RunConfig<'_>) {
+ let builder = run.builder;
+ let host = run.build_triple();
+ let compiler = run.builder.compiler_for(run.builder.top_stage, host, host);
+
+ if builder.doc_tests == DocTests::Only {
+ return;
+ }
+
+ let triple = run.target.triple;
+ let target_supported =
+ if triple.contains("linux") { triple.contains("x86_64") } else { false };
+ if !target_supported {
+ builder.info("target not supported by rustc_codegen_gcc. skipping");
+ return;
+ }
+
+ if builder.remote_tested(run.target) {
+ builder.info("remote testing is not supported by rustc_codegen_gcc. skipping");
+ return;
+ }
+
+ if !builder.config.rust_codegen_backends.contains(&INTERNER.intern_str("gcc")) {
+ builder.info("gcc not in rust.codegen-backends. skipping");
+ return;
+ }
+
+ builder.ensure(CodegenGCC { compiler, target: run.target });
+ }
+
+ fn run(self, builder: &Builder<'_>) {
+ let compiler = self.compiler;
+ let target = self.target;
+
+ builder.ensure(compile::Std::new_with_extra_rust_args(
+ compiler,
+ target,
+ &["-Csymbol-mangling-version=v0", "-Cpanic=abort"],
+ ));
+
+ // If we're not doing a full bootstrap but we're testing a stage2
+ // version of libstd, then what we're actually testing is the libstd
+ // produced in stage1. Reflect that here by updating the compiler that
+ // we're working with automatically.
+ let compiler = builder.compiler_for(compiler.stage, compiler.host, target);
+
+ let build_cargo = || {
+ let mut cargo = builder.cargo(
+ compiler,
+ Mode::Codegen, // Must be codegen to ensure dlopen on compiled dylibs works
+ SourceType::InTree,
+ target,
+ "run",
+ );
+ cargo.current_dir(&builder.src.join("compiler/rustc_codegen_gcc"));
+ cargo
+ .arg("--manifest-path")
+ .arg(builder.src.join("compiler/rustc_codegen_gcc/build_system/Cargo.toml"));
+ compile::rustc_cargo_env(builder, &mut cargo, target, compiler.stage);
+
+ // Avoid incremental cache issues when changing rustc
+ cargo.env("CARGO_BUILD_INCREMENTAL", "false");
+ cargo.rustflag("-Cpanic=abort");
+
+ cargo
+ };
+
+ builder.info(&format!(
+ "{} GCC stage{} ({} -> {})",
+ Kind::Test.description(),
+ compiler.stage,
+ &compiler.host,
+ target
+ ));
+ let _time = helpers::timeit(&builder);
+
+ // FIXME: Uncomment the `prepare` command below once vendoring is implemented.
+ /*
+ let mut prepare_cargo = build_cargo();
+ prepare_cargo.arg("--").arg("prepare");
#[allow(deprecated)]
- builder.config.try_run(&mut cargo.into()).unwrap();
+ builder.config.try_run(&mut prepare_cargo.into()).unwrap();
+ */
+
+ let mut cargo = build_cargo();
+
+ cargo
+ .arg("--")
+ .arg("test")
+ .arg("--use-system-gcc")
+ .arg("--use-backend")
+ .arg("gcc")
+ .arg("--out-dir")
+ .arg(builder.stage_out(compiler, Mode::ToolRustc).join("cg_gcc"))
+ .arg("--release")
+ .arg("--no-default-features")
+ .arg("--mini-tests")
+ .arg("--std-tests");
+ cargo.args(builder.config.test_args());
+
+ let mut cmd: Command = cargo.into();
+ builder.run_cmd(BootstrapCommand::from(&mut cmd).fail_fast());
}
}
diff --git a/src/bootstrap/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs
index f094dd9d7..d1bc05e51 100644
--- a/src/bootstrap/tool.rs
+++ b/src/bootstrap/src/core/build_steps/tool.rs
@@ -3,12 +3,13 @@ use std::fs;
use std::path::PathBuf;
use std::process::Command;
-use crate::builder::{Builder, Cargo as CargoCommand, RunConfig, ShouldRun, Step};
-use crate::channel::GitInfo;
-use crate::compile;
-use crate::config::TargetSelection;
-use crate::toolstate::ToolState;
-use crate::util::{add_dylib_path, exe, t};
+use crate::core::build_steps::compile;
+use crate::core::build_steps::toolstate::ToolState;
+use crate::core::builder::{Builder, Cargo as CargoCommand, RunConfig, ShouldRun, Step};
+use crate::core::config::TargetSelection;
+use crate::utils::channel::GitInfo;
+use crate::utils::exec::BootstrapCommand;
+use crate::utils::helpers::{add_dylib_path, exe, t};
use crate::Compiler;
use crate::Mode;
use crate::{gha, Kind};
@@ -108,8 +109,8 @@ impl Step for ToolBuild {
);
let mut cargo = Command::from(cargo);
- #[allow(deprecated)] // we check this in `is_optional_tool` in a second
- let is_expected = builder.config.try_run(&mut cargo).is_ok();
+ // we check this in `is_optional_tool` in a second
+ let is_expected = builder.run_cmd(BootstrapCommand::from(&mut cargo).allow_failure());
builder.save_toolstate(
tool,
@@ -421,7 +422,7 @@ impl Step for Rustdoc {
fn make_run(run: RunConfig<'_>) {
run.builder.ensure(Rustdoc {
- // Note: this is somewhat unique in that we actually want a *target*
+ // NOTE: this is somewhat unique in that we actually want a *target*
// compiler here, because rustdoc *is* a compiler. We won't be using
// this as the compiler to build with, but rather this is "what
// compiler are we producing"?
@@ -453,7 +454,7 @@ impl Step for Rustdoc {
// compiler, since you do just as much work.
if !builder.config.dry_run() && builder.download_rustc() && build_compiler.stage == 0 {
println!(
- "warning: `download-rustc` does nothing when building stage1 tools; consider using `--stage 2` instead"
+ "WARNING: `download-rustc` does nothing when building stage1 tools; consider using `--stage 2` instead"
);
}
@@ -786,9 +787,9 @@ macro_rules! tool_extended {
}
}
-// Note: tools need to be also added to `Builder::get_step_descriptions` in `builder.rs`
+// NOTE: tools need to be also added to `Builder::get_step_descriptions` in `builder.rs`
// to make `./x.py build <tool>` work.
-// Note: Most submodule updates for tools are handled by bootstrap.py, since they're needed just to
+// NOTE: Most submodule updates for tools are handled by bootstrap.py, since they're needed just to
// invoke Cargo to build bootstrap. See the comment there for more details.
tool_extended!((self, builder),
Cargofmt, "src/tools/rustfmt", "cargo-fmt", stable=true;
diff --git a/src/bootstrap/toolstate.rs b/src/bootstrap/src/core/build_steps/toolstate.rs
index 308023537..a451f92b6 100644
--- a/src/bootstrap/toolstate.rs
+++ b/src/bootstrap/src/core/build_steps/toolstate.rs
@@ -1,5 +1,5 @@
-use crate::builder::{Builder, RunConfig, ShouldRun, Step};
-use crate::util::t;
+use crate::core::builder::{Builder, RunConfig, ShouldRun, Step};
+use crate::utils::helpers::t;
use serde_derive::{Deserialize, Serialize};
use std::collections::HashMap;
use std::env;
@@ -172,7 +172,7 @@ impl Step for ToolStateCheck {
for (tool, _) in STABLE_TOOLS.iter().chain(NIGHTLY_TOOLS.iter()) {
if !toolstates.contains_key(*tool) {
did_error = true;
- eprintln!("error: Tool `{tool}` was not recorded in tool state.");
+ eprintln!("ERROR: Tool `{tool}` was not recorded in tool state.");
}
}
@@ -190,7 +190,7 @@ impl Step for ToolStateCheck {
if state != ToolState::TestPass {
if !is_nightly {
did_error = true;
- eprintln!("error: Tool `{tool}` should be test-pass but is {state}");
+ eprintln!("ERROR: Tool `{tool}` should be test-pass but is {state}");
} else if in_beta_week {
let old_state = old_toolstate
.iter()
@@ -200,14 +200,14 @@ impl Step for ToolStateCheck {
if state < old_state {
did_error = true;
eprintln!(
- "error: Tool `{tool}` has regressed from {old_state} to {state} during beta week."
+ "ERROR: Tool `{tool}` has regressed from {old_state} to {state} during beta week."
);
} else {
// This warning only appears in the logs, which most
// people won't read. It's mostly here for testing and
// debugging.
eprintln!(
- "warning: Tool `{tool}` is not test-pass (is `{state}`), \
+ "WARNING: Tool `{tool}` is not test-pass (is `{state}`), \
this should be fixed before beta is branched."
);
}
diff --git a/src/bootstrap/builder.rs b/src/bootstrap/src/core/builder.rs
index 46a62eed9..cd276674d 100644
--- a/src/bootstrap/builder.rs
+++ b/src/bootstrap/src/core/builder.rs
@@ -2,7 +2,7 @@ use std::any::{type_name, Any};
use std::cell::{Cell, RefCell};
use std::collections::BTreeSet;
use std::env;
-use std::ffi::OsStr;
+use std::ffi::{OsStr, OsString};
use std::fmt::{Debug, Write};
use std::fs::{self, File};
use std::hash::Hash;
@@ -12,20 +12,15 @@ use std::path::{Path, PathBuf};
use std::process::Command;
use std::time::{Duration, Instant};
-use crate::cache::{Cache, Interned, INTERNER};
-use crate::config::{DryRun, SplitDebuginfo, TargetSelection};
-use crate::doc;
-use crate::flags::{Color, Subcommand};
-use crate::install;
-use crate::llvm;
-use crate::run;
-use crate::setup;
-use crate::test;
-use crate::tool::{self, SourceType};
-use crate::util::{self, add_dylib_path, add_link_lib_path, exe, libdir, output, t};
+use crate::core::build_steps::llvm;
+use crate::core::build_steps::tool::{self, SourceType};
+use crate::core::build_steps::{check, clean, compile, dist, doc, install, run, setup, test};
+use crate::core::config::flags::{Color, Subcommand};
+use crate::core::config::{DryRun, SplitDebuginfo, TargetSelection};
+use crate::utils::cache::{Cache, Interned, INTERNER};
+use crate::utils::helpers::{self, add_dylib_path, add_link_lib_path, exe, libdir, output, t};
+use crate::Crate;
use crate::EXTRA_CHECK_CFGS;
-use crate::{check, compile, Crate};
-use crate::{clean, dist};
use crate::{Build, CLang, DocTests, GitRepo, Mode};
pub use crate::Compiler;
@@ -36,6 +31,10 @@ pub use crate::Compiler;
use clap::ValueEnum;
use once_cell::sync::{Lazy, OnceCell};
+#[cfg(test)]
+#[path = "../tests/builder.rs"]
+mod tests;
+
pub struct Builder<'a> {
pub build: &'a Build,
pub top_stage: u32,
@@ -387,13 +386,13 @@ impl StepDescription {
}
if !paths.is_empty() {
- eprintln!("error: no `{}` rules matched {:?}", builder.kind.as_str(), paths,);
+ eprintln!("ERROR: no `{}` rules matched {:?}", builder.kind.as_str(), paths,);
eprintln!(
- "help: run `x.py {} --help --verbose` to show a list of available paths",
+ "HELP: run `x.py {} --help --verbose` to show a list of available paths",
builder.kind.as_str()
);
eprintln!(
- "note: if you are adding a new Step to bootstrap itself, make sure you register it with `describe!`"
+ "NOTE: if you are adding a new Step to bootstrap itself, make sure you register it with `describe!`"
);
crate::exit!(1);
}
@@ -723,13 +722,14 @@ impl<'a> Builder<'a> {
check::Bootstrap
),
Kind::Test => describe!(
- crate::toolstate::ToolStateCheck,
+ crate::core::build_steps::toolstate::ToolStateCheck,
test::ExpandYamlAnchors,
test::Tidy,
test::Ui,
test::RunPassValgrind,
+ test::Coverage,
test::CoverageMap,
- test::RunCoverage,
+ test::CoverageRun,
test::MirOpt,
test::Codegen,
test::CodegenUnits,
@@ -738,8 +738,9 @@ impl<'a> Builder<'a> {
test::Debuginfo,
test::UiFullDeps,
test::CodegenCranelift,
+ test::CodegenGCC,
test::Rustdoc,
- test::RunCoverageRustdoc,
+ test::CoverageRunRustdoc,
test::Pretty,
test::Crate,
test::CrateLibrustc,
@@ -816,6 +817,7 @@ impl<'a> Builder<'a> {
dist::JsonDocs,
dist::Mingw,
dist::Rustc,
+ dist::CodegenBackend,
dist::Std,
dist::RustcDev,
dist::Analysis,
@@ -1174,9 +1176,6 @@ impl<'a> Builder<'a> {
if let Some(linker) = self.linker(compiler.host) {
cmd.env("RUSTDOC_LINKER", linker);
}
- if self.is_fuse_ld_lld(compiler.host) {
- cmd.env("RUSTDOC_FUSE_LD_LLD", "1");
- }
cmd
}
@@ -1268,6 +1267,8 @@ impl<'a> Builder<'a> {
let mut cargo = self.bare_cargo(compiler, mode, target, cmd);
let out_dir = self.stage_out(compiler, mode);
+ let mut hostflags = HostFlags::default();
+
// Codegen backends are not yet tracked by -Zbinary-dep-depinfo,
// so we need to explicitly clear out if they've been updated.
for backend in self.codegen_backends(compiler) {
@@ -1298,8 +1299,8 @@ impl<'a> Builder<'a> {
// See comment in rustc_llvm/build.rs for why this is necessary, largely llvm-config
// needs to not accidentally link to libLLVM in stage0/lib.
- cargo.env("REAL_LIBRARY_PATH_VAR", &util::dylib_path_var());
- if let Some(e) = env::var_os(util::dylib_path_var()) {
+ cargo.env("REAL_LIBRARY_PATH_VAR", &helpers::dylib_path_var());
+ if let Some(e) = env::var_os(helpers::dylib_path_var()) {
cargo.env("REAL_LIBRARY_PATH", e);
}
@@ -1312,7 +1313,7 @@ impl<'a> Builder<'a> {
// rustc_llvm. But if LLVM is stale, that'll be a tiny amount
// of work comparatively, and we'd likely need to rebuild it anyway,
// so that's okay.
- if crate::llvm::prebuilt_llvm_config(self, target).is_err() {
+ if crate::core::build_steps::llvm::prebuilt_llvm_config(self, target).is_err() {
cargo.env("RUST_CHECK", "1");
}
}
@@ -1359,9 +1360,9 @@ impl<'a> Builder<'a> {
}
}).unwrap_or_else(|_| {
eprintln!(
- "error: `x.py clippy` requires a host `rustc` toolchain with the `clippy` component"
+ "ERROR: `x.py clippy` requires a host `rustc` toolchain with the `clippy` component"
);
- eprintln!("help: try `rustup component add clippy`");
+ eprintln!("HELP: try `rustup component add clippy`");
crate::exit!(1);
});
if !t!(std::str::from_utf8(&output.stdout)).contains("nightly") {
@@ -1403,19 +1404,28 @@ impl<'a> Builder<'a> {
rustflags.arg("-Zunstable-options");
}
- // Enable cfg checking of cargo features for everything but std and also enable cfg
- // checking of names and values.
+ // #[cfg(bootstrap)]
+ let use_new_check_cfg_syntax = self.local_rebuild;
+
+ // Enable compile-time checking of `cfg` names, values and Cargo `features`.
//
// Note: `std`, `alloc` and `core` imports some dependencies by #[path] (like
// backtrace, core_simd, std_float, ...), those dependencies have their own
// features but cargo isn't involved in the #[path] process and so cannot pass the
// complete list of features, so for that reason we don't enable checking of
// features for std crates.
- cargo.arg(if mode != Mode::Std {
- "-Zcheck-cfg=names,values,output,features"
+ if use_new_check_cfg_syntax {
+ cargo.arg("-Zcheck-cfg");
+ if mode == Mode::Std {
+ rustflags.arg("--check-cfg=cfg(feature,values(any()))");
+ }
} else {
- "-Zcheck-cfg=names,values,output"
- });
+ cargo.arg(if mode != Mode::Std {
+ "-Zcheck-cfg=names,values,output,features"
+ } else {
+ "-Zcheck-cfg=names,values,output"
+ });
+ }
// Add extra cfg not defined in/by rustc
//
@@ -1435,10 +1445,33 @@ impl<'a> Builder<'a> {
.collect::<String>(),
None => String::new(),
};
- rustflags.arg(&format!("--check-cfg=values({name}{values})"));
+ if use_new_check_cfg_syntax {
+ let values = values.strip_prefix(",").unwrap_or(&values); // remove the first `,`
+ rustflags.arg(&format!("--check-cfg=cfg({name},values({values}))"));
+ } else {
+ rustflags.arg(&format!("--check-cfg=values({name}{values})"));
+ }
}
}
+ // FIXME(rust-lang/cargo#5754) we shouldn't be using special command arguments
+ // to the host invocation here, but rather Cargo should know what flags to pass rustc
+ // itself.
+ if stage == 0 {
+ hostflags.arg("--cfg=bootstrap");
+ }
+ // Cargo doesn't pass RUSTFLAGS to proc_macros:
+ // https://github.com/rust-lang/cargo/issues/4423
+ // Thus, if we are on stage 0, we explicitly set `--cfg=bootstrap`.
+ // We also declare that the flag is expected, which we need to do to not
+ // get warnings about it being unexpected.
+ hostflags.arg("-Zunstable-options");
+ if use_new_check_cfg_syntax {
+ hostflags.arg("--check-cfg=cfg(bootstrap)");
+ } else {
+ hostflags.arg("--check-cfg=values(bootstrap)");
+ }
+
// FIXME: It might be better to use the same value for both `RUSTFLAGS` and `RUSTDOCFLAGS`,
// but this breaks CI. At the very least, stage0 `rustdoc` needs `--cfg bootstrap`. See
// #71458.
@@ -1630,7 +1663,7 @@ impl<'a> Builder<'a> {
// argument manually via `-C link-args=-Wl,-rpath,...`. Plus isn't it
// fun to pass a flag to a tool to pass a flag to pass a flag to a tool
// to change a flag in a binary?
- if self.config.rpath_enabled(target) && util::use_host_linker(target) {
+ if self.config.rpath_enabled(target) && helpers::use_host_linker(target) {
let libdir = self.sysroot_libdir_relative(compiler).to_str().unwrap();
let rpath = if target.contains("apple") {
// Note that we need to take one extra step on macOS to also pass
@@ -1655,11 +1688,10 @@ impl<'a> Builder<'a> {
}
if let Some(host_linker) = self.linker(compiler.host) {
- cargo.env("RUSTC_HOST_LINKER", host_linker);
+ hostflags.arg(format!("-Clinker={}", host_linker.display()));
}
if self.is_fuse_ld_lld(compiler.host) {
- cargo.env("RUSTC_HOST_FUSE_LD_LLD", "1");
- cargo.env("RUSTDOC_FUSE_LD_LLD", "1");
+ hostflags.arg("-Clink-args=-fuse-ld=lld");
}
if let Some(target_linker) = self.linker(target) {
@@ -1743,7 +1775,8 @@ impl<'a> Builder<'a> {
}
if let Some(x) = self.crt_static(compiler.host) {
- cargo.env("RUSTC_HOST_CRT_STATIC", x.to_string());
+ let sign = if x { "+" } else { "-" };
+ hostflags.arg(format!("-Ctarget-feature={sign}crt-static"));
}
if let Some(map_to) = self.build.debuginfo_map_to(GitRepo::Rustc) {
@@ -1755,6 +1788,20 @@ impl<'a> Builder<'a> {
cargo.env("CFG_VIRTUAL_RUST_SOURCE_BASE_DIR", map_to);
}
+ if self.config.rust_remap_debuginfo {
+ // FIXME: handle vendored sources
+ let registry_src = t!(home::cargo_home()).join("registry").join("src");
+ let mut env_var = OsString::new();
+ for entry in t!(std::fs::read_dir(registry_src)) {
+ if !env_var.is_empty() {
+ env_var.push("\t");
+ }
+ env_var.push(t!(entry).path());
+ env_var.push("=/rust/deps");
+ }
+ cargo.env("RUSTC_CARGO_REGISTRY_SRC_TO_REMAP", env_var);
+ }
+
// Enable usage of unstable features
cargo.env("RUSTC_BOOTSTRAP", "1");
self.add_rust_test_threads(&mut cargo);
@@ -2055,7 +2102,7 @@ impl<'a> Builder<'a> {
cargo.env("RUSTFLAGS", &rustc_args.join(" "));
}
- Cargo { command: cargo, rustflags, rustdocflags, allow_features }
+ Cargo { command: cargo, rustflags, rustdocflags, hostflags, allow_features }
}
/// Ensure that a given step is built, returning its output. This will
@@ -2184,9 +2231,6 @@ impl<'a> Builder<'a> {
}
}
-#[cfg(test)]
-mod tests;
-
/// Represents flag values in `String` form with whitespace delimiter to pass it to the compiler later.
///
/// `-Z crate-attr` flags will be applied recursively on the target code using the `rustc_parse::parser::Parser`.
@@ -2233,11 +2277,36 @@ impl Rustflags {
}
}
+/// Flags that are passed to the `rustc` shim binary.
+/// These flags will only be applied when compiling host code, i.e. when
+/// `--target` is unset.
+#[derive(Debug, Default)]
+pub struct HostFlags {
+ rustc: Vec<String>,
+}
+
+impl HostFlags {
+ const SEPARATOR: &'static str = " ";
+
+ /// Adds a host rustc flag.
+ fn arg<S: Into<String>>(&mut self, flag: S) {
+ let value = flag.into().trim().to_string();
+ assert!(!value.contains(Self::SEPARATOR));
+ self.rustc.push(value);
+ }
+
+ /// Encodes all the flags into a single string.
+ fn encode(self) -> String {
+ self.rustc.join(Self::SEPARATOR)
+ }
+}
+
#[derive(Debug)]
pub struct Cargo {
command: Command,
rustflags: Rustflags,
rustdocflags: Rustflags,
+ hostflags: HostFlags,
allow_features: String,
}
@@ -2309,6 +2378,11 @@ impl From<Cargo> for Command {
cargo.command.env("RUSTDOCFLAGS", rustdocflags);
}
+ let encoded_hostflags = cargo.hostflags.encode();
+ if !encoded_hostflags.is_empty() {
+ cargo.command.env("RUSTC_HOST_FLAGS", encoded_hostflags);
+ }
+
if !cargo.allow_features.is_empty() {
cargo.command.env("RUSTC_ALLOW_FEATURES", cargo.allow_features);
}
diff --git a/src/bootstrap/config.rs b/src/bootstrap/src/core/config/config.rs
index 836328f94..0a9175aa3 100644
--- a/src/bootstrap/config.rs
+++ b/src/bootstrap/src/core/config/config.rs
@@ -4,6 +4,7 @@
//! how the build runs.
#[cfg(test)]
+#[path = "../../tests/config.rs"]
mod tests;
use std::cell::{Cell, RefCell};
@@ -17,19 +18,21 @@ use std::path::{Path, PathBuf};
use std::process::Command;
use std::str::FromStr;
-use crate::cache::{Interned, INTERNER};
-use crate::cc_detect::{ndk_compiler, Language};
-use crate::channel::{self, GitInfo};
-use crate::compile::CODEGEN_BACKEND_PREFIX;
-pub use crate::flags::Subcommand;
-use crate::flags::{Color, Flags, Warnings};
-use crate::util::{exe, output, t};
+use crate::core::build_steps::compile::CODEGEN_BACKEND_PREFIX;
+use crate::core::build_steps::llvm;
+use crate::core::config::flags::{Color, Flags, Warnings};
+use crate::utils::cache::{Interned, INTERNER};
+use crate::utils::channel::{self, GitInfo};
+use crate::utils::helpers::{exe, output, t};
use build_helper::exit;
use once_cell::sync::OnceCell;
use semver::Version;
use serde::{Deserialize, Deserializer};
use serde_derive::Deserialize;
+pub use crate::core::config::flags::Subcommand;
+use build_helper::git::GitConfig;
+
macro_rules! check_ci_llvm {
($name:expr) => {
assert!(
@@ -112,7 +115,8 @@ impl Display for DebuginfoLevel {
/// `config.example.toml`.
#[derive(Default, Clone)]
pub struct Config {
- pub changelog_seen: Option<usize>,
+ pub changelog_seen: Option<usize>, // FIXME: Deprecated field. Remove it at 2024.
+ pub change_id: Option<usize>,
pub ccache: Option<String>,
/// Call Build::ninja() instead of this.
pub ninja_in_file: bool,
@@ -139,6 +143,7 @@ pub struct Config {
pub color: Color,
pub patch_binaries_for_nix: Option<bool>,
pub stage0_metadata: Stage0Metadata,
+ pub android_ndk: Option<PathBuf>,
pub stdout_is_tty: bool,
pub stderr_is_tty: bool,
@@ -233,6 +238,7 @@ pub struct Config {
pub llvm_profile_use: Option<String>,
pub llvm_profile_generate: bool,
pub llvm_libunwind_default: Option<LlvmLibunwind>,
+ pub enable_bolt_settings: bool,
pub reproducible_artifacts: Vec<String>,
@@ -314,6 +320,7 @@ pub struct Stage0Config {
pub artifacts_server: String,
pub artifacts_with_llvm_assertions_server: String,
pub git_merge_commit_email: String,
+ pub git_repository: String,
pub nightly_branch: String,
}
#[derive(Default, Deserialize, Clone)]
@@ -517,7 +524,6 @@ pub struct Target {
pub ranlib: Option<PathBuf>,
pub default_linker: Option<PathBuf>,
pub linker: Option<PathBuf>,
- pub ndk: Option<PathBuf>,
pub sanitizers: Option<bool>,
pub profiler: Option<StringOrBool>,
pub rpath: Option<bool>,
@@ -545,8 +551,9 @@ impl Target {
/// `Config` structure.
#[derive(Deserialize, Default)]
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
-struct TomlConfig {
- changelog_seen: Option<usize>,
+pub(crate) struct TomlConfig {
+ changelog_seen: Option<usize>, // FIXME: Deprecated field. Remove it at 2024.
+ change_id: Option<usize>,
build: Option<Build>,
install: Option<Install>,
llvm: Option<Llvm>,
@@ -574,7 +581,17 @@ trait Merge {
impl Merge for TomlConfig {
fn merge(
&mut self,
- TomlConfig { build, install, llvm, rust, dist, target, profile: _, changelog_seen }: Self,
+ TomlConfig {
+ build,
+ install,
+ llvm,
+ rust,
+ dist,
+ target,
+ profile: _,
+ changelog_seen,
+ change_id,
+ }: Self,
replace: ReplaceOpt,
) {
fn do_merge<T: Merge>(x: &mut Option<T>, y: Option<T>, replace: ReplaceOpt) {
@@ -587,6 +604,7 @@ impl Merge for TomlConfig {
}
}
self.changelog_seen.merge(changelog_seen, replace);
+ self.change_id.merge(change_id, replace);
do_merge(&mut self.build, build, replace);
do_merge(&mut self.install, install, replace);
do_merge(&mut self.llvm, llvm, replace);
@@ -783,6 +801,7 @@ define_config! {
patch_binaries_for_nix: Option<bool> = "patch-binaries-for-nix",
// NOTE: only parsed by bootstrap.py, `--feature build-metrics` enables metrics unconditionally
metrics: Option<bool> = "metrics",
+ android_ndk: Option<PathBuf> = "android-ndk",
}
}
@@ -1023,7 +1042,6 @@ define_config! {
llvm_has_rust_patches: Option<bool> = "llvm-has-rust-patches",
llvm_filecheck: Option<String> = "llvm-filecheck",
llvm_libunwind: Option<String> = "llvm-libunwind",
- android_ndk: Option<String> = "android-ndk",
sanitizers: Option<bool> = "sanitizers",
profiler: Option<StringOrBool> = "profiler",
rpath: Option<bool> = "rpath",
@@ -1057,6 +1075,7 @@ impl Config {
config.bindir = "bin".into();
config.dist_include_mingw_linker = true;
config.dist_compression_profile = "fast".into();
+ config.rustc_parallel = true;
config.stdout_is_tty = std::io::stdout().is_terminal();
config.stderr_is_tty = std::io::stderr().is_terminal();
@@ -1115,6 +1134,7 @@ impl Config {
config.free_args = std::mem::take(&mut flags.free_args);
config.llvm_profile_use = flags.llvm_profile_use;
config.llvm_profile_generate = flags.llvm_profile_generate;
+ config.enable_bolt_settings = flags.enable_bolt_settings;
// Infer the rest of the configuration.
@@ -1242,6 +1262,7 @@ impl Config {
toml.merge(override_toml, ReplaceOpt::Override);
config.changelog_seen = toml.changelog_seen;
+ config.change_id = toml.change_id;
let build = toml.build.unwrap_or_default();
if let Some(file_build) = build.build {
@@ -1253,12 +1274,13 @@ impl Config {
// To avoid writing to random places on the file system, `config.out` needs to be an absolute path.
if !config.out.is_absolute() {
// `canonicalize` requires the path to already exist. Use our vendored copy of `absolute` instead.
- config.out = crate::util::absolute(&config.out);
+ config.out = crate::utils::helpers::absolute(&config.out);
}
config.initial_rustc = if let Some(rustc) = build.rustc {
- // FIXME(#115065): re-enable this check
- // config.check_build_rustc_version(&rustc);
+ if !flags.skip_stage0_validation {
+ config.check_build_rustc_version(&rustc);
+ }
PathBuf::from(rustc)
} else {
config.download_beta_toolchain();
@@ -1302,6 +1324,7 @@ impl Config {
config.python = build.python.map(PathBuf::from);
config.reuse = build.reuse.map(PathBuf::from);
config.submodules = build.submodules;
+ config.android_ndk = build.android_ndk;
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);
@@ -1410,7 +1433,9 @@ impl Config {
set(&mut config.use_lld, rust.use_lld);
set(&mut config.lld_enabled, rust.lld);
set(&mut config.llvm_tools_enabled, rust.llvm_tools);
- config.rustc_parallel = rust.parallel_compiler.unwrap_or(false);
+ config.rustc_parallel = rust
+ .parallel_compiler
+ .unwrap_or(config.channel == "dev" || config.channel == "nightly");
config.rustc_default_linker = rust.default_linker;
config.musl_root = rust.musl_root.map(PathBuf::from);
config.save_toolstates = rust.save_toolstates.map(PathBuf::from);
@@ -1439,7 +1464,7 @@ impl Config {
if available_backends.contains(&backend) {
panic!("Invalid value '{s}' for 'rust.codegen-backends'. Instead, please use '{backend}'.");
} else {
- println!("help: '{s}' for 'rust.codegen-backends' might fail. \
+ println!("HELP: '{s}' for 'rust.codegen-backends' might fail. \
Codegen backends are mostly defined without the '{CODEGEN_BACKEND_PREFIX}' prefix. \
In this case, it would be referred to as '{backend}'.");
}
@@ -1508,16 +1533,7 @@ impl Config {
config.llvm_build_config = llvm.build_config.clone().unwrap_or(Default::default());
let asserts = llvm_assertions.unwrap_or(false);
- config.llvm_from_ci = match llvm.download_ci_llvm {
- Some(StringOrBool::String(s)) => {
- assert_eq!(s, "if-available", "unknown option `{s}` for download-ci-llvm");
- crate::llvm::is_ci_llvm_available(&config, asserts)
- }
- Some(StringOrBool::Bool(b)) => b,
- None => {
- config.channel == "dev" && crate::llvm::is_ci_llvm_available(&config, asserts)
- }
- };
+ config.llvm_from_ci = config.parse_download_ci_llvm(llvm.download_ci_llvm, asserts);
if config.llvm_from_ci {
// None of the LLVM options, except assertions, are supported
@@ -1557,8 +1573,8 @@ impl Config {
config.llvm_link_shared.set(Some(true));
}
} else {
- config.llvm_from_ci =
- config.channel == "dev" && crate::llvm::is_ci_llvm_available(&config, false);
+ config.llvm_from_ci = config.channel == "dev"
+ && crate::core::build_steps::llvm::is_ci_llvm_available(&config, false);
}
if let Some(t) = toml.target {
@@ -1581,18 +1597,11 @@ impl Config {
.llvm_libunwind
.as_ref()
.map(|v| v.parse().expect("failed to parse rust.llvm-libunwind"));
- if let Some(ref s) = cfg.android_ndk {
- target.ndk = Some(config.src.join(s));
- }
if let Some(s) = cfg.no_std {
target.no_std = s;
}
- target.cc = cfg.cc.map(PathBuf::from).or_else(|| {
- target.ndk.as_ref().map(|ndk| ndk_compiler(Language::C, &triple, ndk))
- });
- target.cxx = cfg.cxx.map(PathBuf::from).or_else(|| {
- target.ndk.as_ref().map(|ndk| ndk_compiler(Language::CPlusPlus, &triple, ndk))
- });
+ target.cc = cfg.cc.map(PathBuf::from);
+ target.cxx = cfg.cxx.map(PathBuf::from);
target.ar = cfg.ar.map(PathBuf::from);
target.ranlib = cfg.ranlib.map(PathBuf::from);
target.linker = cfg.linker.map(PathBuf::from);
@@ -1799,9 +1808,9 @@ impl Config {
}
(channel, version) => {
let src = self.src.display();
- eprintln!("error: failed to determine artifact channel and/or version");
+ eprintln!("ERROR: failed to determine artifact channel and/or version");
eprintln!(
- "help: consider using a git checkout or ensure these files are readable"
+ "HELP: consider using a git checkout or ensure these files are readable"
);
if let Err(channel) = channel {
eprintln!("reading {src}/src/ci/channel failed: {channel:?}");
@@ -1993,6 +2002,13 @@ impl Config {
self.rust_codegen_backends.get(0).cloned()
}
+ pub fn git_config(&self) -> GitConfig<'_> {
+ GitConfig {
+ git_repository: &self.stage0_metadata.config.git_repository,
+ nightly_branch: &self.stage0_metadata.config.nightly_branch,
+ }
+ }
+
pub fn check_build_rustc_version(&self, rustc_path: &str) {
if self.dry_run() {
return;
@@ -2057,10 +2073,10 @@ impl Config {
);
let commit = merge_base.trim_end();
if commit.is_empty() {
- println!("error: could not find commit hash for downloading rustc");
- println!("help: maybe your repository history is too shallow?");
- println!("help: consider disabling `download-rustc`");
- println!("help: or fetch enough history to include one upstream commit");
+ println!("ERROR: could not find commit hash for downloading rustc");
+ println!("HELP: maybe your repository history is too shallow?");
+ println!("HELP: consider disabling `download-rustc`");
+ println!("HELP: or fetch enough history to include one upstream commit");
crate::exit!(1);
}
@@ -2074,20 +2090,108 @@ impl Config {
if if_unchanged {
if self.verbose > 0 {
println!(
- "warning: saw changes to compiler/ or library/ since {commit}; \
+ "WARNING: saw changes to compiler/ or library/ since {commit}; \
ignoring `download-rustc`"
);
}
return None;
}
println!(
- "warning: `download-rustc` is enabled, but there are changes to \
+ "WARNING: `download-rustc` is enabled, but there are changes to \
compiler/ or library/"
);
}
Some(commit.to_string())
}
+
+ fn parse_download_ci_llvm(
+ &self,
+ download_ci_llvm: Option<StringOrBool>,
+ asserts: bool,
+ ) -> bool {
+ match download_ci_llvm {
+ None => self.channel == "dev" && llvm::is_ci_llvm_available(&self, asserts),
+ Some(StringOrBool::Bool(b)) => b,
+ Some(StringOrBool::String(s)) if s == "if-available" => {
+ llvm::is_ci_llvm_available(&self, asserts)
+ }
+ Some(StringOrBool::String(s)) if s == "if-unchanged" => {
+ // Git is needed to track modifications here, but tarball source is not available.
+ // If not modified here or built through tarball source, we maintain consistency
+ // with '"if available"'.
+ if !self.rust_info.is_from_tarball()
+ && self
+ .last_modified_commit(&["src/llvm-project"], "download-ci-llvm", true)
+ .is_none()
+ {
+ // there are some untracked changes in the the given paths.
+ false
+ } else {
+ llvm::is_ci_llvm_available(&self, asserts)
+ }
+ }
+ Some(StringOrBool::String(other)) => {
+ panic!("unrecognized option for download-ci-llvm: {:?}", other)
+ }
+ }
+ }
+
+ /// Returns the last commit in which any of `modified_paths` were changed,
+ /// or `None` if there are untracked changes in the working directory and `if_unchanged` is true.
+ pub fn last_modified_commit(
+ &self,
+ modified_paths: &[&str],
+ option_name: &str,
+ if_unchanged: bool,
+ ) -> Option<String> {
+ // Handle running from a directory other than the top level
+ let top_level = output(self.git().args(&["rev-parse", "--show-toplevel"]));
+ let top_level = top_level.trim_end();
+
+ // Look for a version to compare to based on the current commit.
+ // Only commits merged by bors will have CI artifacts.
+ let merge_base = output(
+ self.git()
+ .arg("rev-list")
+ .arg(format!("--author={}", self.stage0_metadata.config.git_merge_commit_email))
+ .args(&["-n1", "--first-parent", "HEAD"]),
+ );
+ let commit = merge_base.trim_end();
+ if commit.is_empty() {
+ println!("error: could not find commit hash for downloading components from CI");
+ println!("help: maybe your repository history is too shallow?");
+ println!("help: consider disabling `{option_name}`");
+ println!("help: or fetch enough history to include one upstream commit");
+ crate::exit!(1);
+ }
+
+ // Warn if there were changes to the compiler or standard library since the ancestor commit.
+ let mut git = self.git();
+ git.args(&["diff-index", "--quiet", &commit, "--"]);
+
+ for path in modified_paths {
+ git.arg(format!("{top_level}/{path}"));
+ }
+
+ let has_changes = !t!(git.status()).success();
+ if has_changes {
+ if if_unchanged {
+ if self.verbose > 0 {
+ println!(
+ "warning: saw changes to one of {modified_paths:?} since {commit}; \
+ ignoring `{option_name}`"
+ );
+ }
+ return None;
+ }
+ println!(
+ "warning: `{option_name}` is enabled, but there are changes to one of {modified_paths:?}"
+ );
+ }
+
+ Some(commit.to_string())
+ }
}
fn set<T>(field: &mut T, val: Option<T>) {
diff --git a/src/bootstrap/flags.rs b/src/bootstrap/src/core/config/flags.rs
index e0291e407..2a301007a 100644
--- a/src/bootstrap/flags.rs
+++ b/src/bootstrap/src/core/config/flags.rs
@@ -7,9 +7,9 @@ use std::path::{Path, PathBuf};
use clap::{CommandFactory, Parser, ValueEnum};
-use crate::builder::{Builder, Kind};
-use crate::config::{target_selection_list, Config, TargetSelectionList};
-use crate::setup::Profile;
+use crate::core::build_steps::setup::Profile;
+use crate::core::builder::{Builder, Kind};
+use crate::core::config::{target_selection_list, Config, TargetSelectionList};
use crate::{Build, DocTests};
#[derive(Copy, Clone, Default, Debug, ValueEnum)]
@@ -152,6 +152,12 @@ pub struct Flags {
/// generate PGO profile with llvm built for rustc
#[arg(global(true), long)]
pub llvm_profile_generate: bool,
+ /// Enable BOLT link flags
+ #[arg(global(true), long)]
+ pub enable_bolt_settings: bool,
+ /// Skip stage0 compiler validation
+ #[arg(global(true), long)]
+ pub skip_stage0_validation: bool,
/// Additional reproducible artifacts that should be added to the reproducible artifacts archive.
#[arg(global(true), long)]
pub reproducible_artifact: Vec<String>,
@@ -184,7 +190,7 @@ impl Flags {
if let Ok(HelpVerboseOnly { help: true, verbose: 1.., cmd: subcommand }) =
HelpVerboseOnly::try_parse_from(it.clone())
{
- println!("note: updating submodules before printing available paths");
+ println!("NOTE: updating submodules before printing available paths");
let config = Config::parse(&[String::from("build")]);
let build = Build::new(config);
let paths = Builder::get_help(&build, subcommand);
diff --git a/src/bootstrap/src/core/config/mod.rs b/src/bootstrap/src/core/config/mod.rs
new file mode 100644
index 000000000..9c6861826
--- /dev/null
+++ b/src/bootstrap/src/core/config/mod.rs
@@ -0,0 +1,4 @@
+pub(crate) mod config;
+pub(crate) mod flags;
+
+pub use config::*;
diff --git a/src/bootstrap/download.rs b/src/bootstrap/src/core/download.rs
index 8e9614ec8..3327aed96 100644
--- a/src/bootstrap/download.rs
+++ b/src/bootstrap/src/core/download.rs
@@ -11,13 +11,10 @@ use build_helper::ci::CiEnv;
use once_cell::sync::OnceCell;
use xz2::bufread::XzDecoder;
-use crate::{
- config::RustfmtMetadata,
- llvm::detect_llvm_sha,
- t,
- util::{check_run, exe, program_out_of_date},
- Config,
-};
+use crate::core::build_steps::llvm::detect_llvm_sha;
+use crate::core::config::RustfmtMetadata;
+use crate::utils::helpers::{check_run, exe, program_out_of_date};
+use crate::{t, Config};
static SHOULD_FIX_BINS_AND_DYLIBS: OnceCell<bool> = OnceCell::new();
@@ -117,7 +114,7 @@ impl Config {
is_nixos
});
if val {
- eprintln!("info: You seem to be using Nix.");
+ eprintln!("INFO: You seem to be using Nix.");
}
val
}
@@ -320,25 +317,43 @@ impl Config {
}
/// Returns whether the SHA256 checksum of `path` matches `expected`.
- fn verify(&self, path: &Path, expected: &str) -> bool {
+ pub(crate) fn verify(&self, path: &Path, expected: &str) -> bool {
use sha2::Digest;
self.verbose(&format!("verifying {}", path.display()));
+
+ if self.dry_run() {
+ return false;
+ }
+
let mut hasher = sha2::Sha256::new();
- // FIXME: this is ok for rustfmt (4.1 MB large at time of writing), but it seems memory-intensive for rustc and larger components.
- // Consider using streaming IO instead?
- let contents = if self.dry_run() { vec![] } else { t!(fs::read(path)) };
- hasher.update(&contents);
- let found = hex::encode(hasher.finalize().as_slice());
- let verified = found == expected;
- if !verified && !self.dry_run() {
+
+ let file = t!(File::open(path));
+ let mut reader = BufReader::new(file);
+
+ loop {
+ let buffer = t!(reader.fill_buf());
+ let l = buffer.len();
+ // break if EOF
+ if l == 0 {
+ break;
+ }
+ hasher.update(buffer);
+ reader.consume(l);
+ }
+
+ let checksum = hex::encode(hasher.finalize().as_slice());
+ let verified = checksum == expected;
+
+ if !verified {
println!(
"invalid checksum: \n\
- found: {found}\n\
+ found: {checksum}\n\
expected: {expected}",
);
}
- return verified;
+
+ verified
}
}
@@ -591,10 +606,10 @@ impl Config {
let mut help_on_error = "";
if destination == "ci-rustc" {
- help_on_error = "error: failed to download pre-built rustc from CI
+ help_on_error = "ERROR: failed to download pre-built rustc from CI
-note: old builds get deleted after a certain time
-help: if trying to compile an old commit of rustc, disable `download-rustc` in config.toml:
+NOTE: old builds get deleted after a certain time
+HELP: if trying to compile an old commit of rustc, disable `download-rustc` in config.toml:
[rust]
download-rustc = false
@@ -670,10 +685,10 @@ download-rustc = false
let filename = format!("rust-dev-{}-{}.tar.xz", version, self.build.triple);
let tarball = rustc_cache.join(&filename);
if !tarball.exists() {
- let help_on_error = "error: failed to download llvm from ci
+ let help_on_error = "ERROR: failed to download llvm from ci
- help: old builds get deleted after a certain time
- help: if trying to compile an old commit of rustc, disable `download-ci-llvm` in config.toml:
+ HELP: old builds get deleted after a certain time
+ HELP: if trying to compile an old commit of rustc, disable `download-ci-llvm` in config.toml:
[llvm]
download-ci-llvm = false
diff --git a/src/bootstrap/metadata.rs b/src/bootstrap/src/core/metadata.rs
index 3b20ceac8..580208232 100644
--- a/src/bootstrap/metadata.rs
+++ b/src/bootstrap/src/core/metadata.rs
@@ -3,8 +3,8 @@ use std::process::Command;
use serde_derive::Deserialize;
-use crate::cache::INTERNER;
-use crate::util::output;
+use crate::utils::cache::INTERNER;
+use crate::utils::helpers::output;
use crate::{t, Build, Crate};
/// For more information, see the output of
diff --git a/src/bootstrap/src/core/mod.rs b/src/bootstrap/src/core/mod.rs
new file mode 100644
index 000000000..9e18d6704
--- /dev/null
+++ b/src/bootstrap/src/core/mod.rs
@@ -0,0 +1,6 @@
+pub(crate) mod build_steps;
+pub(crate) mod builder;
+pub(crate) mod config;
+pub(crate) mod download;
+pub(crate) mod metadata;
+pub(crate) mod sanity;
diff --git a/src/bootstrap/sanity.rs b/src/bootstrap/src/core/sanity.rs
index 0febdf250..eec3be66a 100644
--- a/src/bootstrap/sanity.rs
+++ b/src/bootstrap/src/core/sanity.rs
@@ -15,9 +15,9 @@ use std::fs;
use std::path::PathBuf;
use std::process::Command;
-use crate::cache::INTERNER;
-use crate::config::Target;
-use crate::util::output;
+use crate::core::config::Target;
+use crate::utils::cache::INTERNER;
+use crate::utils::helpers::output;
use crate::Build;
pub struct Finder {
diff --git a/src/bootstrap/lib.rs b/src/bootstrap/src/lib.rs
index 8b8d4b237..33b8f1a7c 100644
--- a/src/bootstrap/lib.rs
+++ b/src/bootstrap/src/lib.rs
@@ -23,75 +23,32 @@ use std::fmt::Display;
use std::fs::{self, File};
use std::io;
use std::path::{Path, PathBuf};
-use std::process::{Command, Stdio};
+use std::process::{Command, Output, Stdio};
use std::str;
use build_helper::ci::{gha, CiEnv};
use build_helper::exit;
-use channel::GitInfo;
-use config::{DryRun, Target};
+use build_helper::util::fail;
use filetime::FileTime;
use once_cell::sync::OnceCell;
+use termcolor::{ColorChoice, StandardStream, WriteColor};
+use utils::channel::GitInfo;
-use crate::builder::Kind;
-use crate::config::{LlvmLibunwind, TargetSelection};
-use crate::util::{
- dir_is_empty, exe, libdir, mtime, output, run, run_suppressed, symlink_dir, try_run_suppressed,
-};
-
-mod builder;
-mod cache;
-mod cc_detect;
-mod channel;
-mod check;
-mod clean;
-mod compile;
-mod config;
-mod dist;
-mod doc;
-mod download;
-mod flags;
-mod format;
-mod install;
-mod llvm;
-mod metadata;
-mod render_tests;
-mod run;
-mod sanity;
-mod setup;
-mod suggest;
-mod synthetic_targets;
-mod tarball;
-mod test;
-mod tool;
-mod toolstate;
-pub mod util;
-
-#[cfg(feature = "build-metrics")]
-mod metrics;
-
-#[cfg(windows)]
-mod job;
-
-#[cfg(all(unix, not(target_os = "haiku")))]
-mod job {
- pub unsafe fn setup(build: &mut crate::Build) {
- if build.config.low_priority {
- libc::setpriority(libc::PRIO_PGRP as _, 0, 10);
- }
- }
-}
+use crate::core::builder;
+use crate::core::builder::Kind;
+use crate::core::config::flags;
+use crate::core::config::{DryRun, Target};
+use crate::core::config::{LlvmLibunwind, TargetSelection};
+use crate::utils::cache::{Interned, INTERNER};
+use crate::utils::exec::{BehaviorOnFailure, BootstrapCommand, OutputMode};
+use crate::utils::helpers::{self, dir_is_empty, exe, libdir, mtime, output, symlink_dir};
-#[cfg(any(target_os = "haiku", target_os = "hermit", not(any(unix, windows))))]
-mod job {
- pub unsafe fn setup(_build: &mut crate::Build) {}
-}
+mod core;
+mod utils;
-pub use crate::builder::PathSet;
-use crate::cache::{Interned, INTERNER};
-pub use crate::config::Config;
-pub use crate::flags::Subcommand;
-use termcolor::{ColorChoice, StandardStream, WriteColor};
+pub use crate::core::builder::PathSet;
+pub use crate::core::config::flags::Subcommand;
+pub use crate::core::config::Config;
const LLVM_TOOLS: &[&str] = &[
"llvm-cov", // used to generate coverage report
@@ -112,7 +69,16 @@ const LLVM_TOOLS: &[&str] = &[
/// LLD file names for all flavors.
const LLD_FILE_NAMES: &[&str] = &["ld.lld", "ld64.lld", "lld-link", "wasm-ld"];
-pub const VERSION: usize = 2;
+/// Keeps track of major changes made to the bootstrap configuration.
+///
+/// These values also represent the IDs of the PRs that caused major changes.
+/// You can visit `https://github.com/rust-lang/rust/pull/{any-id-from-the-list}` to
+/// check for more details regarding each change.
+///
+/// If you make any major changes (such as adding new values or changing default values),
+/// please ensure that the associated PR ID is added to the end of this list.
+/// This is necessary because the list must be sorted by the merge date.
+pub const CONFIG_CHANGE_HISTORY: &[usize] = &[115898, 116998, 117435, 116881];
/// Extra --check-cfg to add when building
/// (Mode restriction, config name, config values (if any))
@@ -130,18 +96,9 @@ const EXTRA_CHECK_CFGS: &[(Option<Mode>, &str, Option<&[&'static str]>)] = &[
(Some(Mode::Std), "freebsd13", None),
(Some(Mode::Std), "backtrace_in_libstd", None),
/* Extra values not defined in the built-in targets yet, but used in std */
- // #[cfg(bootstrap)]
- (Some(Mode::Std), "target_vendor", Some(&["unikraft"])),
(Some(Mode::Std), "target_env", Some(&["libnx"])),
- // #[cfg(bootstrap)] hurd
- (Some(Mode::Std), "target_os", Some(&["teeos", "hurd"])),
- (Some(Mode::Rustc), "target_os", Some(&["hurd"])),
- // #[cfg(bootstrap)] mips32r6, mips64r6
- (
- Some(Mode::Std),
- "target_arch",
- Some(&["asmjs", "spirv", "nvptx", "xtensa", "mips32r6", "mips64r6", "csky"]),
- ),
+ // (Some(Mode::Std), "target_os", Some(&[])),
+ (Some(Mode::Std), "target_arch", Some(&["asmjs", "spirv", "nvptx", "xtensa"])),
/* Extra names used by dependencies */
// FIXME: Used by serde_json, but we should not be triggering on external dependencies.
(Some(Mode::Rustc), "no_btreemap_remove_entry", None),
@@ -211,12 +168,12 @@ pub struct Build {
src: PathBuf,
out: PathBuf,
bootstrap_out: PathBuf,
- cargo_info: channel::GitInfo,
- rust_analyzer_info: channel::GitInfo,
- clippy_info: channel::GitInfo,
- miri_info: channel::GitInfo,
- rustfmt_info: channel::GitInfo,
- in_tree_llvm_info: channel::GitInfo,
+ cargo_info: GitInfo,
+ rust_analyzer_info: GitInfo,
+ clippy_info: GitInfo,
+ miri_info: GitInfo,
+ rustfmt_info: GitInfo,
+ in_tree_llvm_info: GitInfo,
local_rebuild: bool,
fail_fast: bool,
doc_tests: DocTests,
@@ -249,7 +206,7 @@ pub struct Build {
prerelease_version: Cell<Option<u32>>,
#[cfg(feature = "build-metrics")]
- metrics: metrics::BuildMetrics,
+ metrics: crate::utils::metrics::BuildMetrics,
}
#[derive(Debug, Clone)]
@@ -360,6 +317,10 @@ impl Build {
// https://github.com/rust-lang/rust/blob/a8a33cf27166d3eabaffc58ed3799e054af3b0c6/src/bootstrap/bootstrap.py#L796-L797
let is_sudo = match env::var_os("SUDO_USER") {
Some(_sudo_user) => {
+ // SAFETY: getuid() system call is always successful and no return value is reserved
+ // to indicate an error.
+ //
+ // For more context, see https://man7.org/linux/man-pages/man2/geteuid.2.html
let uid = unsafe { libc::getuid() };
uid == 0
}
@@ -369,16 +330,15 @@ impl Build {
let is_sudo = false;
let omit_git_hash = config.omit_git_hash;
- let rust_info = channel::GitInfo::new(omit_git_hash, &src);
- let cargo_info = channel::GitInfo::new(omit_git_hash, &src.join("src/tools/cargo"));
- let rust_analyzer_info =
- channel::GitInfo::new(omit_git_hash, &src.join("src/tools/rust-analyzer"));
- let clippy_info = channel::GitInfo::new(omit_git_hash, &src.join("src/tools/clippy"));
- let miri_info = channel::GitInfo::new(omit_git_hash, &src.join("src/tools/miri"));
- let rustfmt_info = channel::GitInfo::new(omit_git_hash, &src.join("src/tools/rustfmt"));
+ let rust_info = GitInfo::new(omit_git_hash, &src);
+ let cargo_info = GitInfo::new(omit_git_hash, &src.join("src/tools/cargo"));
+ let rust_analyzer_info = GitInfo::new(omit_git_hash, &src.join("src/tools/rust-analyzer"));
+ let clippy_info = GitInfo::new(omit_git_hash, &src.join("src/tools/clippy"));
+ let miri_info = GitInfo::new(omit_git_hash, &src.join("src/tools/miri"));
+ let rustfmt_info = GitInfo::new(omit_git_hash, &src.join("src/tools/rustfmt"));
// we always try to use git for LLVM builds
- let in_tree_llvm_info = channel::GitInfo::new(false, &src.join("src/llvm-project"));
+ let in_tree_llvm_info = GitInfo::new(false, &src.join("src/llvm-project"));
let initial_target_libdir_str = if config.dry_run() {
"/dummy/lib/path/to/lib/".to_string()
@@ -471,7 +431,7 @@ impl Build {
prerelease_version: Cell::new(None),
#[cfg(feature = "build-metrics")]
- metrics: metrics::BuildMetrics::init(),
+ metrics: crate::utils::metrics::BuildMetrics::init(),
};
// If local-rust is the same major.minor as the current version, then force a
@@ -490,7 +450,7 @@ impl Build {
}
build.verbose("finding compilers");
- cc_detect::find(&build);
+ utils::cc_detect::find(&build);
// When running `setup`, the profile is about to change, so any requirements we have now may
// be different on the next invocation. Don't check for them until the next time x.py is
// run. This is ok because `setup` never runs any build commands, so it won't fail if commands are missing.
@@ -498,7 +458,7 @@ impl Build {
// Similarly, for `setup` we don't actually need submodules or cargo metadata.
if !matches!(build.config.cmd, Subcommand::Setup { .. }) {
build.verbose("running sanity check");
- sanity::check(&mut build);
+ crate::core::sanity::check(&mut build);
// Make sure we update these before gathering metadata so we don't get an error about missing
// Cargo.toml files.
@@ -510,7 +470,7 @@ impl Build {
build.update_existing_submodules();
build.verbose("learning about cargo");
- metadata::build(&mut build);
+ crate::core::metadata::build(&mut build);
}
// Make a symbolic link so we can use a consistent directory in the documentation.
@@ -546,7 +506,7 @@ impl Build {
// NOTE: The check for the empty directory is here because when running x.py the first time,
// the submodule won't be checked out. Check it out now so we can build it.
- if !channel::GitInfo::new(false, &absolute_path).is_managed_git_subrepository()
+ if !GitInfo::new(false, &absolute_path).is_managed_git_subrepository()
&& !dir_is_empty(&absolute_path)
{
return;
@@ -620,15 +580,19 @@ impl Build {
}
// Save any local changes, but avoid running `git stash pop` if there are none (since it will exit with an error).
- #[allow(deprecated)] // diff-index reports the modifications through the exit status
- let has_local_modifications = self
- .config
- .try_run(
+ // diff-index reports the modifications through the exit status
+ let has_local_modifications = !self.run_cmd(
+ BootstrapCommand::from(
Command::new("git")
.args(&["diff-index", "--quiet", "HEAD"])
.current_dir(&absolute_path),
)
- .is_err();
+ .allow_failure()
+ .output_mode(match self.is_verbose() {
+ true => OutputMode::PrintAll,
+ false => OutputMode::PrintOutput,
+ }),
+ );
if has_local_modifications {
self.run(Command::new("git").args(&["stash", "push"]).current_dir(&absolute_path));
}
@@ -660,7 +624,7 @@ impl Build {
// Sample output: `submodule.src/rust-installer.path src/tools/rust-installer`
let submodule = Path::new(line.splitn(2, ' ').nth(1).unwrap());
// Don't update the submodule unless it's already been cloned.
- if channel::GitInfo::new(false, submodule).is_managed_git_subrepository() {
+ if GitInfo::new(false, submodule).is_managed_git_subrepository() {
self.update_submodule(submodule);
}
}
@@ -669,7 +633,7 @@ impl Build {
/// Executes the entire build, as configured by the flags and configuration.
pub fn build(&mut self) {
unsafe {
- job::setup(self);
+ crate::utils::job::setup(self);
}
// Download rustfmt early so that it can be used in rust-analyzer configs.
@@ -678,10 +642,14 @@ impl Build {
// hardcoded subcommands
match &self.config.cmd {
Subcommand::Format { check } => {
- return format::format(&builder::Builder::new(&self), *check, &self.config.paths);
+ return core::build_steps::format::format(
+ &builder::Builder::new(&self),
+ *check,
+ &self.config.paths,
+ );
}
Subcommand::Suggest { run } => {
- return suggest::suggest(&builder::Builder::new(&self), *run);
+ return core::build_steps::suggest::suggest(&builder::Builder::new(&self), *run);
}
_ => (),
}
@@ -957,55 +925,103 @@ impl Build {
/// Runs a command, printing out nice contextual information if it fails.
fn run(&self, cmd: &mut Command) {
- if self.config.dry_run() {
- return;
- }
- self.verbose(&format!("running: {cmd:?}"));
- run(cmd, self.is_verbose())
+ self.run_cmd(BootstrapCommand::from(cmd).fail_fast().output_mode(
+ match self.is_verbose() {
+ true => OutputMode::PrintAll,
+ false => OutputMode::PrintOutput,
+ },
+ ));
+ }
+
+ /// Runs a command, printing out contextual info if it fails, and delaying errors until the build finishes.
+ pub(crate) fn run_delaying_failure(&self, cmd: &mut Command) -> bool {
+ self.run_cmd(BootstrapCommand::from(cmd).delay_failure().output_mode(
+ match self.is_verbose() {
+ true => OutputMode::PrintAll,
+ false => OutputMode::PrintOutput,
+ },
+ ))
}
/// Runs a command, printing out nice contextual information if it fails.
fn run_quiet(&self, cmd: &mut Command) {
- if self.config.dry_run() {
- return;
- }
- self.verbose(&format!("running: {cmd:?}"));
- run_suppressed(cmd)
+ self.run_cmd(
+ BootstrapCommand::from(cmd).fail_fast().output_mode(OutputMode::SuppressOnSuccess),
+ );
}
/// Runs a command, printing out nice contextual information if it fails.
/// Exits if the command failed to execute at all, otherwise returns its
/// `status.success()`.
fn run_quiet_delaying_failure(&self, cmd: &mut Command) -> bool {
+ self.run_cmd(
+ BootstrapCommand::from(cmd).delay_failure().output_mode(OutputMode::SuppressOnSuccess),
+ )
+ }
+
+ /// A centralized function for running commands that do not return output.
+ pub(crate) fn run_cmd<'a, C: Into<BootstrapCommand<'a>>>(&self, cmd: C) -> bool {
if self.config.dry_run() {
return true;
}
- if !self.fail_fast {
- self.verbose(&format!("running: {cmd:?}"));
- if !try_run_suppressed(cmd) {
- let mut failures = self.delayed_failures.borrow_mut();
- failures.push(format!("{cmd:?}"));
- return false;
+
+ let command = cmd.into();
+ self.verbose(&format!("running: {command:?}"));
+
+ let (output, print_error) = match command.output_mode {
+ mode @ (OutputMode::PrintAll | OutputMode::PrintOutput) => (
+ command.command.status().map(|status| Output {
+ status,
+ stdout: Vec::new(),
+ stderr: Vec::new(),
+ }),
+ matches!(mode, OutputMode::PrintAll),
+ ),
+ OutputMode::SuppressOnSuccess => (command.command.output(), true),
+ };
+
+ let output = match output {
+ Ok(output) => output,
+ Err(e) => fail(&format!("failed to execute command: {:?}\nerror: {}", command, e)),
+ };
+ let result = if !output.status.success() {
+ if print_error {
+ println!(
+ "\n\ncommand did not execute successfully: {:?}\n\
+ expected success, got: {}\n\n\
+ stdout ----\n{}\n\
+ stderr ----\n{}\n\n",
+ command.command,
+ output.status,
+ String::from_utf8_lossy(&output.stdout),
+ String::from_utf8_lossy(&output.stderr)
+ );
}
+ Err(())
} else {
- self.run_quiet(cmd);
- }
- true
- }
+ Ok(())
+ };
- /// Runs a command, printing out contextual info if it fails, and delaying errors until the build finishes.
- pub(crate) fn run_delaying_failure(&self, cmd: &mut Command) -> bool {
- if !self.fail_fast {
- #[allow(deprecated)] // can't use Build::try_run, that's us
- if self.config.try_run(cmd).is_err() {
- let mut failures = self.delayed_failures.borrow_mut();
- failures.push(format!("{cmd:?}"));
- return false;
+ match result {
+ Ok(_) => true,
+ Err(_) => {
+ match command.failure_behavior {
+ BehaviorOnFailure::DelayFail => {
+ if self.fail_fast {
+ exit!(1);
+ }
+
+ let mut failures = self.delayed_failures.borrow_mut();
+ failures.push(format!("{command:?}"));
+ }
+ BehaviorOnFailure::Exit => {
+ exit!(1);
+ }
+ BehaviorOnFailure::Ignore => {}
+ }
+ false
}
- } else {
- self.run(cmd);
}
- true
}
pub fn is_verbose_than(&self, level: usize) -> bool {
@@ -1062,7 +1078,7 @@ impl Build {
/// Return a `Group` guard for a [`Step`] that is built for each `--stage`.
///
- /// [`Step`]: crate::builder::Step
+ /// [`Step`]: crate::core::builder::Step
#[must_use = "Groups should not be dropped until the Step finishes running"]
#[track_caller]
fn msg(
@@ -1090,7 +1106,7 @@ impl Build {
/// Return a `Group` guard for a [`Step`] that is only built once and isn't affected by `--stage`.
///
- /// [`Step`]: crate::builder::Step
+ /// [`Step`]: crate::core::builder::Step
#[must_use = "Groups should not be dropped until the Step finishes running"]
#[track_caller]
fn msg_unstaged(
@@ -1182,11 +1198,10 @@ impl Build {
.filter(|s| !s.starts_with("-O") && !s.starts_with("/O"))
.collect::<Vec<String>>();
- // If we're compiling on macOS then we add a few unconditional flags
- // indicating that we want libc++ (more filled out than libstdc++) and
- // we want to compile for 10.7. This way we can ensure that
+ // If we're compiling C++ on macOS then we add a flag indicating that
+ // we want libc++ (more filled out than libstdc++), ensuring that
// LLVM/etc are all properly compiled.
- if target.contains("apple-darwin") {
+ if matches!(c, CLang::Cxx) && target.contains("apple-darwin") {
base.push("-stdlib=libc++".into());
}
@@ -1250,7 +1265,7 @@ impl Build {
// that are only existed in CXX libraries
Some(self.cxx.borrow()[&target].path().into())
} else if target != self.config.build
- && util::use_host_linker(target)
+ && helpers::use_host_linker(target)
&& !target.contains("msvc")
{
Some(self.cc(target))
@@ -1275,7 +1290,7 @@ impl Build {
options[0] = Some("-Clink-arg=-fuse-ld=lld".to_string());
}
- let no_threads = util::lld_flag_no_threads(target.contains("windows"));
+ let no_threads = helpers::lld_flag_no_threads(target.contains("windows"));
options[1] = Some(format!("-Clink-arg=-Wl,{no_threads}"));
}
@@ -1415,7 +1430,7 @@ impl Build {
fn extract_beta_rev_from_file<P: AsRef<Path>>(version_file: P) -> Option<String> {
let version = fs::read_to_string(version_file).ok()?;
- extract_beta_rev(&version)
+ helpers::extract_beta_rev(&version)
}
if let Some(s) = self.prerelease_version.get() {
@@ -1553,7 +1568,7 @@ impl Build {
if !stamp.exists() {
eprintln!(
- "Error: Unable to find the stamp file {}, did you try to keep a nonexistent build stage?",
+ "ERROR: Unable to find the stamp file {}, did you try to keep a nonexistent build stage?",
stamp.display()
);
crate::exit!(1);
@@ -1682,7 +1697,7 @@ impl Build {
self.verbose_than(1, &format!("Install {src:?} to {dst:?}"));
t!(fs::create_dir_all(dstdir));
if !src.exists() {
- panic!("Error: File \"{}\" not found!", src.display());
+ panic!("ERROR: File \"{}\" not found!", src.display());
}
self.copy_internal(src, &dst, true);
chmod(&dst, perms);
@@ -1729,7 +1744,7 @@ impl Build {
/// Returns if config.ninja is enabled, and checks for ninja existence,
/// exiting with a nicer error message if not.
fn ninja(&self) -> bool {
- let mut cmd_finder = crate::sanity::Finder::new();
+ let mut cmd_finder = crate::core::sanity::Finder::new();
if self.config.ninja_in_file {
// Some Linux distros rename `ninja` to `ninja-build`.
@@ -1795,17 +1810,6 @@ to download LLVM rather than building it.
}
}
-/// Extract the beta revision from the full version string.
-///
-/// The full version string looks like "a.b.c-beta.y". And we need to extract
-/// the "y" part from the string.
-pub fn extract_beta_rev(version: &str) -> Option<String> {
- let parts = version.splitn(2, "-beta.").collect::<Vec<_>>();
- let count = parts.get(1).and_then(|s| s.find(' ').map(|p| (&s[..p]).to_string()));
-
- count
-}
-
#[cfg(unix)]
fn chmod(path: &Path, perms: u32) {
use std::os::unix::fs::*;
@@ -1844,3 +1848,27 @@ fn envify(s: &str) -> String {
.flat_map(|c| c.to_uppercase())
.collect()
}
+
+pub fn find_recent_config_change_ids(current_id: usize) -> Vec<usize> {
+ if !CONFIG_CHANGE_HISTORY.contains(&current_id) {
+ // If the current change-id is greater than the most recent one, return
+ // an empty list (it may be due to switching from a recent branch to an
+ // older one); otherwise, return the full list (assuming the user provided
+ // the incorrect change-id by accident).
+ if let Some(max_id) = CONFIG_CHANGE_HISTORY.iter().max() {
+ if &current_id > max_id {
+ return Vec::new();
+ }
+ }
+
+ return CONFIG_CHANGE_HISTORY.to_vec();
+ }
+
+ let index = CONFIG_CHANGE_HISTORY.iter().position(|&id| id == current_id).unwrap();
+
+ CONFIG_CHANGE_HISTORY
+ .iter()
+ .skip(index + 1) // Skip the current_id and IDs before it
+ .cloned()
+ .collect()
+}
diff --git a/src/bootstrap/builder/tests.rs b/src/bootstrap/src/tests/builder.rs
index 80e66622e..96139f7b0 100644
--- a/src/bootstrap/builder/tests.rs
+++ b/src/bootstrap/src/tests/builder.rs
@@ -1,6 +1,6 @@
use super::*;
-use crate::config::{Config, DryRun, TargetSelection};
-use crate::doc::DocumentationFormat;
+use crate::core::config::{Config, DryRun, TargetSelection};
+use crate::core::build_steps::doc::DocumentationFormat;
use std::thread;
fn configure(cmd: &str, host: &[&str], target: &[&str]) -> Config {
@@ -22,7 +22,6 @@ fn configure_with_args(cmd: &[String], host: &[&str], target: &[&str]) -> Config
..Config::parse(&["check".to_owned()])
});
submodule_build.update_submodule(Path::new("src/doc/book"));
- submodule_build.update_submodule(Path::new("src/tools/rust-analyzer"));
config.submodules = Some(false);
config.ninja_in_file = false;
@@ -159,7 +158,7 @@ fn alias_and_path_for_library() {
#[test]
fn test_beta_rev_parsing() {
- use crate::extract_beta_rev;
+ use crate::utils::helpers::extract_beta_rev;
// single digit revision
assert_eq!(extract_beta_rev("1.99.9-beta.7 (xxxxxx)"), Some("7".to_string()));
@@ -175,7 +174,7 @@ fn test_beta_rev_parsing() {
mod defaults {
use super::{configure, first, run_build};
- use crate::builder::*;
+ use crate::core::builder::*;
use crate::Config;
use pretty_assertions::assert_eq;
@@ -286,7 +285,7 @@ mod defaults {
mod dist {
use super::{first, run_build, Config};
- use crate::builder::*;
+ use crate::core::builder::*;
use pretty_assertions::assert_eq;
fn configure(host: &[&str], target: &[&str]) -> Config {
diff --git a/src/bootstrap/config/tests.rs b/src/bootstrap/src/tests/config.rs
index aac76cdcb..59bd52a94 100644
--- a/src/bootstrap/config/tests.rs
+++ b/src/bootstrap/src/tests/config.rs
@@ -1,9 +1,14 @@
-use crate::config::TomlConfig;
-
+use crate::core::config::TomlConfig;
use super::{Config, Flags};
+
use clap::CommandFactory;
use serde::Deserialize;
-use std::{env, path::Path};
+use std::{
+ env,
+ fs::{remove_file, File},
+ io::Write,
+ path::Path,
+};
fn parse(config: &str) -> Config {
Config::parse_inner(&["check".to_owned(), "--config=/does/not/exist".to_owned()], |&_| {
@@ -13,7 +18,7 @@ fn parse(config: &str) -> Config {
#[test]
fn download_ci_llvm() {
- if crate::llvm::is_ci_llvm_modified(&parse("")) {
+ if crate::core::build_steps::llvm::is_ci_llvm_modified(&parse("")) {
eprintln!("Detected LLVM as non-available: running in CI and modified LLVM in this change");
return;
}
@@ -102,7 +107,7 @@ fn override_toml() {
&[
"check".to_owned(),
"--config=/does/not/exist".to_owned(),
- "--set=changelog-seen=1".to_owned(),
+ "--set=change-id=1".to_owned(),
"--set=rust.lto=fat".to_owned(),
"--set=rust.deny-warnings=false".to_owned(),
"--set=build.gdb=\"bar\"".to_owned(),
@@ -112,7 +117,7 @@ fn override_toml() {
|&_| {
toml::from_str(
r#"
-changelog-seen = 0
+change-id = 0
[rust]
lto = "off"
deny-warnings = true
@@ -129,10 +134,10 @@ build-config = {}
.unwrap()
},
);
- assert_eq!(config.changelog_seen, Some(1), "setting top-level value");
+ assert_eq!(config.change_id, Some(1), "setting top-level value");
assert_eq!(
config.rust_lto,
- crate::config::RustcLto::Fat,
+ crate::core::config::RustcLto::Fat,
"setting string value without quotes"
);
assert_eq!(config.gdb, Some("bar".into()), "setting string value with quotes");
@@ -156,10 +161,10 @@ fn override_toml_duplicate() {
&[
"check".to_owned(),
"--config=/does/not/exist".to_owned(),
- "--set=changelog-seen=1".to_owned(),
- "--set=changelog-seen=2".to_owned(),
+ "--set=change-id=1".to_owned(),
+ "--set=change-id=2".to_owned(),
],
- |&_| toml::from_str("changelog-seen = 0").unwrap(),
+ |&_| toml::from_str("change-id = 0").unwrap(),
);
}
@@ -170,7 +175,7 @@ fn profile_user_dist() {
"profile = \"user\"".to_owned()
} else {
assert!(file.ends_with("config.dist.toml"));
- std::fs::read_to_string(dbg!(file)).unwrap()
+ std::fs::read_to_string(file).unwrap()
};
toml::from_str(&contents)
.and_then(|table: toml::Value| TomlConfig::deserialize(table))
@@ -196,3 +201,19 @@ fn rust_optimize() {
fn invalid_rust_optimize() {
parse("rust.optimize = \"a\"");
}
+
+#[test]
+fn verify_file_integrity() {
+ let config = parse("");
+
+ let tempfile = config.tempdir().join(".tmp-test-file");
+ File::create(&tempfile).unwrap().write_all(b"dummy value").unwrap();
+ assert!(tempfile.exists());
+
+ assert!(
+ config
+ .verify(&tempfile, "7e255dd9542648a8779268a0f268b891a198e9828e860ed23f826440e786eae5")
+ );
+
+ remove_file(tempfile).unwrap();
+}
diff --git a/src/bootstrap/setup/tests.rs b/src/bootstrap/src/tests/setup.rs
index 0fe6e4a46..0fe6e4a46 100644
--- a/src/bootstrap/setup/tests.rs
+++ b/src/bootstrap/src/tests/setup.rs
diff --git a/src/bootstrap/bin/_helper.rs b/src/bootstrap/src/utils/bin_helpers.rs
index 09aa471db..c90fd2805 100644
--- a/src/bootstrap/bin/_helper.rs
+++ b/src/bootstrap/src/utils/bin_helpers.rs
@@ -1,8 +1,12 @@
+//! This file is meant to be included directly from bootstrap shims to avoid a
+//! dependency on the bootstrap library. This reduces the binary size and
+//! improves compilation time by reducing the linking time.
+
/// Parses the value of the "RUSTC_VERBOSE" environment variable and returns it as a `usize`.
/// If it was not defined, returns 0 by default.
///
/// Panics if "RUSTC_VERBOSE" is defined with the value that is not an unsigned integer.
-fn parse_rustc_verbose() -> usize {
+pub(crate) fn parse_rustc_verbose() -> usize {
use std::str::FromStr;
match std::env::var("RUSTC_VERBOSE") {
@@ -14,11 +18,11 @@ fn parse_rustc_verbose() -> usize {
/// Parses the value of the "RUSTC_STAGE" environment variable and returns it as a `String`.
///
/// If "RUSTC_STAGE" was not set, the program will be terminated with 101.
-fn parse_rustc_stage() -> String {
+pub(crate) fn parse_rustc_stage() -> String {
std::env::var("RUSTC_STAGE").unwrap_or_else(|_| {
// Don't panic here; it's reasonable to try and run these shims directly. Give a helpful error instead.
- eprintln!("rustc shim: fatal: RUSTC_STAGE was not set");
- eprintln!("rustc shim: note: use `x.py build -vvv` to see all environment variables set by bootstrap");
- exit(101);
+ eprintln!("rustc shim: FATAL: RUSTC_STAGE was not set");
+ eprintln!("rustc shim: NOTE: use `x.py build -vvv` to see all environment variables set by bootstrap");
+ std::process::exit(101);
})
}
diff --git a/src/bootstrap/cache.rs b/src/bootstrap/src/utils/cache.rs
index 53e4ff034..1b2aa9c23 100644
--- a/src/bootstrap/cache.rs
+++ b/src/bootstrap/src/utils/cache.rs
@@ -14,7 +14,7 @@ use std::sync::Mutex;
// FIXME: replace with std::lazy after it gets stabilized and reaches beta
use once_cell::sync::Lazy;
-use crate::builder::Step;
+use crate::core::builder::Step;
pub struct Interned<T>(usize, PhantomData<*const T>);
diff --git a/src/bootstrap/cc_detect.rs b/src/bootstrap/src/utils/cc_detect.rs
index 2496c2a9d..52b36ce75 100644
--- a/src/bootstrap/cc_detect.rs
+++ b/src/bootstrap/src/utils/cc_detect.rs
@@ -26,8 +26,8 @@ use std::path::{Path, PathBuf};
use std::process::Command;
use std::{env, iter};
-use crate::config::{Target, TargetSelection};
-use crate::util::output;
+use crate::core::config::TargetSelection;
+use crate::utils::helpers::output;
use crate::{Build, CLang, GitRepo};
// The `cc` crate doesn't provide a way to obtain a path to the detected archiver,
@@ -107,10 +107,11 @@ pub fn find(build: &Build) {
pub fn find_target(build: &Build, target: TargetSelection) {
let mut cfg = new_cc_build(build, target);
let config = build.config.target_config.get(&target);
- if let Some(cc) = config.and_then(|c| c.cc.as_ref()) {
+ if let Some(cc) = config
+ .and_then(|c| c.cc.clone())
+ .or_else(|| default_compiler(&mut cfg, Language::C, target, build))
+ {
cfg.compiler(cc);
- } else {
- set_compiler(&mut cfg, Language::C, target, config, build);
}
let compiler = cfg.get_compiler();
@@ -127,12 +128,12 @@ pub fn find_target(build: &Build, target: TargetSelection) {
// We'll need one anyways if the target triple is also a host triple
let mut cfg = new_cc_build(build, target);
cfg.cpp(true);
- let cxx_configured = if let Some(cxx) = config.and_then(|c| c.cxx.as_ref()) {
+ let cxx_configured = if let Some(cxx) = config
+ .and_then(|c| c.cxx.clone())
+ .or_else(|| default_compiler(&mut cfg, Language::CPlusPlus, target, build))
+ {
cfg.compiler(cxx);
true
- } else if build.hosts.contains(&target) || build.build == target {
- set_compiler(&mut cfg, Language::CPlusPlus, target, config, build);
- true
} else {
// Use an auto-detected compiler (or one configured via `CXX_target_triple` env vars).
cfg.try_get_compiler().is_ok()
@@ -161,22 +162,21 @@ pub fn find_target(build: &Build, target: TargetSelection) {
}
}
-fn set_compiler(
+fn default_compiler(
cfg: &mut cc::Build,
compiler: Language,
target: TargetSelection,
- config: Option<&Target>,
build: &Build,
-) {
+) -> Option<PathBuf> {
match &*target.triple {
// When compiling for android we may have the NDK configured in the
// config.toml in which case we look there. Otherwise the default
// compiler already takes into account the triple in question.
- t if t.contains("android") => {
- if let Some(ndk) = config.and_then(|c| c.ndk.as_ref()) {
- cfg.compiler(ndk_compiler(compiler, &*target.triple, ndk));
- }
- }
+ t if t.contains("android") => build
+ .config
+ .android_ndk
+ .as_ref()
+ .map(|ndk| ndk_compiler(compiler, &*target.triple, ndk)),
// The default gcc version from OpenBSD may be too old, try using egcc,
// which is a gcc version from ports, if this is the case.
@@ -184,45 +184,48 @@ fn set_compiler(
let c = cfg.get_compiler();
let gnu_compiler = compiler.gcc();
if !c.path().ends_with(gnu_compiler) {
- return;
+ return None;
}
let output = output(c.to_command().arg("--version"));
- let i = match output.find(" 4.") {
- Some(i) => i,
- None => return,
- };
+ let i = output.find(" 4.")?;
match output[i + 3..].chars().next().unwrap() {
'0'..='6' => {}
- _ => return,
+ _ => return None,
}
let alternative = format!("e{gnu_compiler}");
if Command::new(&alternative).output().is_ok() {
- cfg.compiler(alternative);
+ Some(PathBuf::from(alternative))
+ } else {
+ None
}
}
- "mips-unknown-linux-musl" => {
+ "mips-unknown-linux-musl" if compiler == Language::C => {
if cfg.get_compiler().path().to_str() == Some("gcc") {
- cfg.compiler("mips-linux-musl-gcc");
+ Some(PathBuf::from("mips-linux-musl-gcc"))
+ } else {
+ None
}
}
- "mipsel-unknown-linux-musl" => {
+ "mipsel-unknown-linux-musl" if compiler == Language::C => {
if cfg.get_compiler().path().to_str() == Some("gcc") {
- cfg.compiler("mipsel-linux-musl-gcc");
+ Some(PathBuf::from("mipsel-linux-musl-gcc"))
+ } else {
+ None
}
}
- t if t.contains("musl") => {
+ t if t.contains("musl") && compiler == Language::C => {
if let Some(root) = build.musl_root(target) {
let guess = root.join("bin/musl-gcc");
- if guess.exists() {
- cfg.compiler(guess);
- }
+ if guess.exists() { Some(guess) } else { None }
+ } else {
+ None
}
}
- _ => {}
+ _ => None,
}
}
@@ -243,10 +246,22 @@ pub(crate) fn ndk_compiler(compiler: Language, triple: &str, ndk: &Path) -> Path
let api_level =
if triple.contains("aarch64") || triple.contains("x86_64") { "21" } else { "19" };
let compiler = format!("{}{}-{}", triple_translated, api_level, compiler.clang());
- ndk.join("bin").join(compiler)
+ let host_tag = if cfg!(target_os = "macos") {
+ // The NDK uses universal binaries, so this is correct even on ARM.
+ "darwin-x86_64"
+ } else if cfg!(target_os = "windows") {
+ "windows-x86_64"
+ } else {
+ // NDK r25b only has official releases for macOS, Windows and Linux.
+ // Try the Linux directory everywhere else, on the assumption that the OS has an
+ // emulation layer that can cope (e.g. BSDs).
+ "linux-x86_64"
+ };
+ ndk.join("toolchains").join("llvm").join("prebuilt").join(host_tag).join("bin").join(compiler)
}
/// The target programming language for a native compiler.
+#[derive(PartialEq)]
pub(crate) enum Language {
/// The compiler is targeting C.
C,
diff --git a/src/bootstrap/channel.rs b/src/bootstrap/src/utils/channel.rs
index 870185740..e59d7f22a 100644
--- a/src/bootstrap/channel.rs
+++ b/src/bootstrap/src/utils/channel.rs
@@ -9,8 +9,7 @@ use std::fs;
use std::path::Path;
use std::process::Command;
-use crate::util::output;
-use crate::util::t;
+use crate::utils::helpers::{output, t};
use crate::Build;
#[derive(Clone, Default)]
diff --git a/src/bootstrap/dylib_util.rs b/src/bootstrap/src/utils/dylib.rs
index b14c0bed6..279a6a010 100644
--- a/src/bootstrap/dylib_util.rs
+++ b/src/bootstrap/src/utils/dylib.rs
@@ -1,7 +1,4 @@
-// Various utilities for working with dylib paths.
-//
-// This file is meant to be included directly to avoid a dependency on the bootstrap library from
-// the rustc and rustdoc wrappers. This improves compilation time by reducing the linking time.
+//! Various utilities for working with dylib paths.
/// Returns the environment variable which the dynamic library lookup path
/// resides in for this platform.
@@ -21,10 +18,10 @@ pub fn dylib_path_var() -> &'static str {
/// Parses the `dylib_path_var()` environment variable, returning a list of
/// paths that are members of this lookup path.
-pub fn dylib_path() -> Vec<PathBuf> {
- let var = match env::var_os(dylib_path_var()) {
+pub fn dylib_path() -> Vec<std::path::PathBuf> {
+ let var = match std::env::var_os(dylib_path_var()) {
Some(v) => v,
None => return vec![],
};
- env::split_paths(&var).collect()
+ std::env::split_paths(&var).collect()
}
diff --git a/src/bootstrap/src/utils/exec.rs b/src/bootstrap/src/utils/exec.rs
new file mode 100644
index 000000000..0aede2022
--- /dev/null
+++ b/src/bootstrap/src/utils/exec.rs
@@ -0,0 +1,60 @@
+use std::process::Command;
+
+/// What should be done when the command fails.
+#[derive(Debug, Copy, Clone)]
+pub enum BehaviorOnFailure {
+ /// Immediately stop bootstrap.
+ Exit,
+ /// Delay failure until the end of bootstrap invocation.
+ DelayFail,
+ /// Ignore the failure, the command can fail in an expected way.
+ Ignore,
+}
+
+/// How should the output of the command be handled.
+#[derive(Debug, Copy, Clone)]
+pub enum OutputMode {
+ /// Print both the output (by inheriting stdout/stderr) and also the command itself, if it
+ /// fails.
+ PrintAll,
+ /// Print the output (by inheriting stdout/stderr).
+ PrintOutput,
+ /// Suppress the output if the command succeeds, otherwise print the output.
+ SuppressOnSuccess,
+}
+
+/// Wrapper around `std::process::Command`.
+#[derive(Debug)]
+pub struct BootstrapCommand<'a> {
+ pub command: &'a mut Command,
+ pub failure_behavior: BehaviorOnFailure,
+ pub output_mode: OutputMode,
+}
+
+impl<'a> BootstrapCommand<'a> {
+ pub fn delay_failure(self) -> Self {
+ Self { failure_behavior: BehaviorOnFailure::DelayFail, ..self }
+ }
+
+ pub fn fail_fast(self) -> Self {
+ Self { failure_behavior: BehaviorOnFailure::Exit, ..self }
+ }
+
+ pub fn allow_failure(self) -> Self {
+ Self { failure_behavior: BehaviorOnFailure::Ignore, ..self }
+ }
+
+ pub fn output_mode(self, output_mode: OutputMode) -> Self {
+ Self { output_mode, ..self }
+ }
+}
+
+impl<'a> From<&'a mut Command> for BootstrapCommand<'a> {
+ fn from(command: &'a mut Command) -> Self {
+ Self {
+ command,
+ failure_behavior: BehaviorOnFailure::Exit,
+ output_mode: OutputMode::PrintAll,
+ }
+ }
+}
diff --git a/src/bootstrap/util.rs b/src/bootstrap/src/utils/helpers.rs
index 3c4a21434..5bc81f2d9 100644
--- a/src/bootstrap/util.rs
+++ b/src/bootstrap/src/utils/helpers.rs
@@ -3,7 +3,7 @@
//! Simple things like testing the various filesystem operations here and there,
//! not a lot of interesting happenings here unfortunately.
-use build_helper::util::{fail, try_run};
+use build_helper::util::fail;
use std::env;
use std::fs;
use std::io;
@@ -12,10 +12,12 @@ use std::process::{Command, Stdio};
use std::str;
use std::time::{Instant, SystemTime, UNIX_EPOCH};
-use crate::builder::Builder;
-use crate::config::{Config, TargetSelection};
+use crate::core::builder::Builder;
+use crate::core::config::{Config, TargetSelection};
use crate::OnceCell;
+pub use crate::utils::dylib::{dylib_path, dylib_path_var};
+
/// A helper macro to `unwrap` a result except also print out details like:
///
/// * The file/line of the panic
@@ -81,8 +83,6 @@ pub fn add_dylib_path(path: Vec<PathBuf>, cmd: &mut Command) {
cmd.env(dylib_path_var(), t!(env::join_paths(list)));
}
-include!("dylib_util.rs");
-
/// Adds a list of lookup paths to `cmd`'s link library lookup path.
pub fn add_link_lib_path(path: Vec<PathBuf>, cmd: &mut Command) {
let mut list = link_lib_path();
@@ -183,6 +183,19 @@ pub fn use_host_linker(target: TargetSelection) -> bool {
|| target.contains("switch"))
}
+pub fn target_supports_cranelift_backend(target: TargetSelection) -> bool {
+ if target.contains("linux") {
+ target.contains("x86_64")
+ || target.contains("aarch64")
+ || target.contains("s390x")
+ || target.contains("riscv64gc")
+ } else if target.contains("darwin") || target.contains("windows") {
+ target.contains("x86_64")
+ } else {
+ false
+ }
+}
+
pub fn is_valid_test_suite_arg<'a, P: AsRef<Path>>(
path: &'a Path,
suite_path: P,
@@ -216,17 +229,11 @@ pub fn is_valid_test_suite_arg<'a, P: AsRef<Path>>(
}
}
-pub fn run(cmd: &mut Command, print_cmd_on_fail: bool) {
- if try_run(cmd, print_cmd_on_fail).is_err() {
- crate::exit!(1);
- }
-}
-
pub fn check_run(cmd: &mut Command, print_cmd_on_fail: bool) -> bool {
let status = match cmd.status() {
Ok(status) => status,
Err(e) => {
- println!("failed to execute command: {cmd:?}\nerror: {e}");
+ println!("failed to execute command: {cmd:?}\nERROR: {e}");
return false;
}
};
@@ -239,32 +246,6 @@ pub fn check_run(cmd: &mut Command, print_cmd_on_fail: bool) -> bool {
status.success()
}
-pub fn run_suppressed(cmd: &mut Command) {
- if !try_run_suppressed(cmd) {
- crate::exit!(1);
- }
-}
-
-pub fn try_run_suppressed(cmd: &mut Command) -> bool {
- let output = match cmd.output() {
- Ok(status) => status,
- Err(e) => fail(&format!("failed to execute command: {cmd:?}\nerror: {e}")),
- };
- if !output.status.success() {
- println!(
- "\n\ncommand did not execute successfully: {:?}\n\
- expected success, got: {}\n\n\
- stdout ----\n{}\n\
- stderr ----\n{}\n\n",
- cmd,
- output.status,
- String::from_utf8_lossy(&output.stdout),
- String::from_utf8_lossy(&output.stderr)
- );
- }
- output.status.success()
-}
-
pub fn make(host: &str) -> PathBuf {
if host.contains("dragonfly")
|| host.contains("freebsd")
@@ -281,7 +262,7 @@ pub fn make(host: &str) -> PathBuf {
pub fn output(cmd: &mut Command) -> String {
let output = match cmd.stderr(Stdio::inherit()).output() {
Ok(status) => status,
- Err(e) => fail(&format!("failed to execute command: {cmd:?}\nerror: {e}")),
+ Err(e) => fail(&format!("failed to execute command: {cmd:?}\nERROR: {e}")),
};
if !output.status.success() {
panic!(
@@ -293,23 +274,6 @@ pub fn output(cmd: &mut Command) -> String {
String::from_utf8(output.stdout).unwrap()
}
-pub fn output_result(cmd: &mut Command) -> Result<String, String> {
- let output = match cmd.stderr(Stdio::inherit()).output() {
- Ok(status) => status,
- Err(e) => return Err(format!("failed to run command: {cmd:?}: {e}")),
- };
- if !output.status.success() {
- return Err(format!(
- "command did not execute successfully: {:?}\n\
- expected success, got: {}\n{}",
- cmd,
- output.status,
- String::from_utf8(output.stderr).map_err(|err| format!("{err:?}"))?
- ));
- }
- Ok(String::from_utf8(output.stdout).map_err(|err| format!("{err:?}"))?)
-}
-
/// Returns the last-modified time for `path`, or zero if it doesn't exist.
pub fn mtime(path: &Path) -> SystemTime {
fs::metadata(path).and_then(|f| f.modified()).unwrap_or(UNIX_EPOCH)
@@ -363,7 +327,7 @@ pub(crate) fn absolute(path: &Path) -> PathBuf {
}
#[cfg(not(any(unix, windows)))]
{
- println!("warning: bootstrap is not supported on non-unix platforms");
+ println!("WARNING: bootstrap is not supported on non-unix platforms");
t!(std::fs::canonicalize(t!(std::env::current_dir()))).join(path)
}
}
@@ -495,3 +459,14 @@ pub fn lld_flag_no_threads(is_windows: bool) -> &'static str {
pub fn dir_is_empty(dir: &Path) -> bool {
t!(std::fs::read_dir(dir)).next().is_none()
}
+
+/// Extract the beta revision from the full version string.
+///
+/// The full version string looks like "a.b.c-beta.y". And we need to extract
+/// the "y" part from the string.
+pub fn extract_beta_rev(version: &str) -> Option<String> {
+ let parts = version.splitn(2, "-beta.").collect::<Vec<_>>();
+ let count = parts.get(1).and_then(|s| s.find(' ').map(|p| (&s[..p]).to_string()));
+
+ count
+}
diff --git a/src/bootstrap/src/utils/job.rs b/src/bootstrap/src/utils/job.rs
new file mode 100644
index 000000000..c5c718a89
--- /dev/null
+++ b/src/bootstrap/src/utils/job.rs
@@ -0,0 +1,159 @@
+#[cfg(windows)]
+pub use for_windows::*;
+
+#[cfg(any(target_os = "haiku", target_os = "hermit", not(any(unix, windows))))]
+pub unsafe fn setup(_build: &mut crate::Build) {}
+
+#[cfg(all(unix, not(target_os = "haiku")))]
+pub unsafe fn setup(build: &mut crate::Build) {
+ if build.config.low_priority {
+ libc::setpriority(libc::PRIO_PGRP as _, 0, 10);
+ }
+}
+
+#[cfg(windows)]
+mod for_windows {
+ //! Job management on Windows for bootstrapping
+ //!
+ //! Most of the time when you're running a build system (e.g., make) you expect
+ //! Ctrl-C or abnormal termination to actually terminate the entire tree of
+ //! process in play, not just the one at the top. This currently works "by
+ //! default" on Unix platforms because Ctrl-C actually sends a signal to the
+ //! *process group* rather than the parent process, so everything will get torn
+ //! down. On Windows, however, this does not happen and Ctrl-C just kills the
+ //! parent process.
+ //!
+ //! To achieve the same semantics on Windows we use Job Objects to ensure that
+ //! all processes die at the same time. Job objects have a mode of operation
+ //! where when all handles to the object are closed it causes all child
+ //! processes associated with the object to be terminated immediately.
+ //! Conveniently whenever a process in the job object spawns a new process the
+ //! child will be associated with the job object as well. This means if we add
+ //! ourselves to the job object we create then everything will get torn down!
+ //!
+ //! Unfortunately most of the time the build system is actually called from a
+ //! python wrapper (which manages things like building the build system) so this
+ //! all doesn't quite cut it so far. To go the last mile we duplicate the job
+ //! object handle into our parent process (a python process probably) and then
+ //! close our own handle. This means that the only handle to the job object
+ //! resides in the parent python process, so when python dies the whole build
+ //! system dies (as one would probably expect!).
+ //!
+ //! Note that this module has a #[cfg(windows)] above it as none of this logic
+ //! is required on Unix.
+
+ use crate::Build;
+ use std::env;
+ use std::ffi::c_void;
+ use std::io;
+ use std::mem;
+
+ use windows::{
+ core::PCWSTR,
+ Win32::Foundation::{CloseHandle, DuplicateHandle, DUPLICATE_SAME_ACCESS, HANDLE},
+ Win32::System::Diagnostics::Debug::{
+ SetErrorMode, SEM_NOGPFAULTERRORBOX, THREAD_ERROR_MODE,
+ },
+ Win32::System::JobObjects::{
+ AssignProcessToJobObject, CreateJobObjectW, JobObjectExtendedLimitInformation,
+ SetInformationJobObject, JOBOBJECT_EXTENDED_LIMIT_INFORMATION,
+ JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE, JOB_OBJECT_LIMIT_PRIORITY_CLASS,
+ },
+ Win32::System::Threading::{
+ GetCurrentProcess, OpenProcess, BELOW_NORMAL_PRIORITY_CLASS, PROCESS_DUP_HANDLE,
+ },
+ };
+
+ pub unsafe fn setup(build: &mut Build) {
+ // Enable the Windows Error Reporting dialog which msys disables,
+ // so we can JIT debug rustc
+ let mode = SetErrorMode(THREAD_ERROR_MODE::default());
+ let mode = THREAD_ERROR_MODE(mode);
+ SetErrorMode(mode & !SEM_NOGPFAULTERRORBOX);
+
+ // Create a new job object for us to use
+ let job = CreateJobObjectW(None, PCWSTR::null()).unwrap();
+
+ // Indicate that when all handles to the job object are gone that all
+ // process in the object should be killed. Note that this includes our
+ // entire process tree by default because we've added ourselves and our
+ // children will reside in the job by default.
+ let mut info = JOBOBJECT_EXTENDED_LIMIT_INFORMATION::default();
+ info.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
+ if build.config.low_priority {
+ info.BasicLimitInformation.LimitFlags |= JOB_OBJECT_LIMIT_PRIORITY_CLASS;
+ info.BasicLimitInformation.PriorityClass = BELOW_NORMAL_PRIORITY_CLASS.0;
+ }
+ let r = SetInformationJobObject(
+ job,
+ JobObjectExtendedLimitInformation,
+ &info as *const _ as *const c_void,
+ mem::size_of_val(&info) as u32,
+ );
+ assert!(r.is_ok(), "{}", io::Error::last_os_error());
+
+ // Assign our process to this job object. Note that if this fails, one very
+ // likely reason is that we are ourselves already in a job object! This can
+ // happen on the build bots that we've got for Windows, or if just anyone
+ // else is instrumenting the build. In this case we just bail out
+ // immediately and assume that they take care of it.
+ //
+ // Also note that nested jobs (why this might fail) are supported in recent
+ // versions of Windows, but the version of Windows that our bots are running
+ // at least don't support nested job objects.
+ let r = AssignProcessToJobObject(job, GetCurrentProcess());
+ if r.is_err() {
+ CloseHandle(job).ok();
+ return;
+ }
+
+ // If we've got a parent process (e.g., the python script that called us)
+ // then move ownership of this job object up to them. That way if the python
+ // script is killed (e.g., via ctrl-c) then we'll all be torn down.
+ //
+ // If we don't have a parent (e.g., this was run directly) then we
+ // intentionally leak the job object handle. When our process exits
+ // (normally or abnormally) it will close the handle implicitly, causing all
+ // processes in the job to be cleaned up.
+ let pid = match env::var("BOOTSTRAP_PARENT_ID") {
+ Ok(s) => s,
+ Err(..) => return,
+ };
+
+ let parent = match OpenProcess(PROCESS_DUP_HANDLE, false, pid.parse().unwrap()).ok() {
+ Some(parent) => parent,
+ _ => {
+ // If we get a null parent pointer here, it is possible that either
+ // we have an invalid pid or the parent process has been closed.
+ // Since the first case rarely happens
+ // (only when wrongly setting the environmental variable),
+ // it might be better to improve the experience of the second case
+ // when users have interrupted the parent process and we haven't finish
+ // duplicating the handle yet. We just need close the job object if that occurs.
+ CloseHandle(job).ok();
+ return;
+ }
+ };
+
+ let mut parent_handle = HANDLE::default();
+ let r = DuplicateHandle(
+ GetCurrentProcess(),
+ job,
+ parent,
+ &mut parent_handle,
+ 0,
+ false,
+ DUPLICATE_SAME_ACCESS,
+ );
+
+ // If this failed, well at least we tried! An example of DuplicateHandle
+ // failing in the past has been when the wrong python2 package spawned this
+ // build system (e.g., the `python2` package in MSYS instead of
+ // `mingw-w64-x86_64-python2`). Not sure why it failed, but the "failure
+ // mode" here is that we only clean everything up when the build system
+ // dies, not when the python parent does, so not too bad.
+ if r.is_err() {
+ CloseHandle(job).ok();
+ }
+ }
+}
diff --git a/src/bootstrap/metrics.rs b/src/bootstrap/src/utils/metrics.rs
index cf8d33dfc..174f37422 100644
--- a/src/bootstrap/metrics.rs
+++ b/src/bootstrap/src/utils/metrics.rs
@@ -4,8 +4,8 @@
//! As this module requires additional dependencies not present during local builds, it's cfg'd
//! away whenever the `build.metrics` config option is not set to `true`.
-use crate::builder::{Builder, Step};
-use crate::util::t;
+use crate::core::builder::{Builder, Step};
+use crate::utils::helpers::t;
use crate::Build;
use build_helper::metrics::{
JsonInvocation, JsonInvocationSystemStats, JsonNode, JsonRoot, JsonStepSystemStats, Test,
@@ -180,7 +180,7 @@ impl BuildMetrics {
t!(serde_json::from_slice::<JsonRoot>(&contents)).invocations
} else {
println!(
- "warning: overriding existing build/metrics.json, as it's not \
+ "WARNING: overriding existing build/metrics.json, as it's not \
compatible with build metrics format version {CURRENT_FORMAT_VERSION}."
);
Vec::new()
diff --git a/src/bootstrap/src/utils/mod.rs b/src/bootstrap/src/utils/mod.rs
new file mode 100644
index 000000000..8ca22d008
--- /dev/null
+++ b/src/bootstrap/src/utils/mod.rs
@@ -0,0 +1,15 @@
+//! This module contains integral components of the build and configuration process, providing
+//! support for a wide range of tasks and operations such as caching, tarballs, release
+//! channels, job management, etc.
+
+pub(crate) mod cache;
+pub(crate) mod cc_detect;
+pub(crate) mod channel;
+pub(crate) mod dylib;
+pub(crate) mod exec;
+pub(crate) mod helpers;
+pub(crate) mod job;
+#[cfg(feature = "build-metrics")]
+pub(crate) mod metrics;
+pub(crate) mod render_tests;
+pub(crate) mod tarball;
diff --git a/src/bootstrap/render_tests.rs b/src/bootstrap/src/utils/render_tests.rs
index 6802bf451..bff47f65c 100644
--- a/src/bootstrap/render_tests.rs
+++ b/src/bootstrap/src/utils/render_tests.rs
@@ -6,7 +6,7 @@
//! and rustc) libtest doesn't include the rendered human-readable output as a JSON field. We had
//! to reimplement all the rendering logic in this module because of that.
-use crate::builder::Builder;
+use crate::core::builder::Builder;
use std::io::{BufRead, BufReader, Read, Write};
use std::process::{ChildStdout, Command, Stdio};
use std::time::Duration;
@@ -197,7 +197,7 @@ impl<'a> Renderer<'a> {
println!("{stdout}");
}
if let Some(message) = &failure.message {
- println!("note: {message}");
+ println!("NOTE: {message}");
}
}
}
diff --git a/src/bootstrap/tarball.rs b/src/bootstrap/src/utils/tarball.rs
index 95d909c57..a8393f88f 100644
--- a/src/bootstrap/tarball.rs
+++ b/src/bootstrap/src/utils/tarball.rs
@@ -3,9 +3,10 @@ use std::{
process::Command,
};
-use crate::builder::Builder;
-use crate::channel;
-use crate::util::t;
+use crate::core::build_steps::dist::distdir;
+use crate::core::builder::Builder;
+use crate::utils::channel;
+use crate::utils::helpers::t;
#[derive(Copy, Clone)]
pub(crate) enum OverlayKind {
@@ -18,6 +19,7 @@ pub(crate) enum OverlayKind {
RustDemangler,
RLS,
RustAnalyzer,
+ RustcCodegenCranelift,
}
impl OverlayKind {
@@ -57,6 +59,11 @@ impl OverlayKind {
"src/tools/rust-analyzer/LICENSE-APACHE",
"src/tools/rust-analyzer/LICENSE-MIT",
],
+ OverlayKind::RustcCodegenCranelift => &[
+ "compiler/rustc_codegen_cranelift/Readme.md",
+ "compiler/rustc_codegen_cranelift/LICENSE-APACHE",
+ "compiler/rustc_codegen_cranelift/LICENSE-MIT",
+ ],
}
}
@@ -79,6 +86,7 @@ impl OverlayKind {
OverlayKind::RustAnalyzer => builder
.rust_analyzer_info
.version(builder, &builder.release_num("rust-analyzer/crates/rust-analyzer")),
+ OverlayKind::RustcCodegenCranelift => builder.rust_version(),
}
}
}
@@ -112,7 +120,7 @@ impl<'a> Tarball<'a> {
}
fn new_inner(builder: &'a Builder<'a>, component: &str, target: Option<String>) -> Self {
- let pkgname = crate::dist::pkgname(builder, component);
+ let pkgname = crate::core::build_steps::dist::pkgname(builder, component);
let mut temp_dir = builder.out.join("tmp").join("tarball").join(component);
if let Some(target) = &target {
@@ -265,7 +273,7 @@ impl<'a> Tarball<'a> {
t!(std::fs::rename(&self.image_dir, &dest));
self.run(|this, cmd| {
- let distdir = crate::dist::distdir(this.builder);
+ let distdir = distdir(this.builder);
t!(std::fs::create_dir_all(&distdir));
cmd.arg("tarball")
.arg("--input")
@@ -292,7 +300,7 @@ impl<'a> Tarball<'a> {
.arg("--non-installed-overlay")
.arg(&self.overlay_dir)
.arg("--output-dir")
- .arg(crate::dist::distdir(self.builder));
+ .arg(distdir(self.builder));
}
fn run(self, build_cli: impl FnOnce(&Tarball<'a>, &mut Command)) -> GeneratedTarball {
@@ -306,11 +314,11 @@ impl<'a> Tarball<'a> {
self.builder.install(&self.builder.src.join(file), &self.overlay_dir, 0o644);
}
- let mut cmd = self.builder.tool_cmd(crate::tool::Tool::RustInstaller);
+ let mut cmd = self.builder.tool_cmd(crate::core::build_steps::tool::Tool::RustInstaller);
let package_name = self.package_name();
self.builder.info(&format!("Dist {package_name}"));
- let _time = crate::util::timeit(self.builder);
+ let _time = crate::utils::helpers::timeit(self.builder);
build_cli(&self, &mut cmd);
cmd.arg("--work-dir").arg(&self.temp_dir);
@@ -344,7 +352,7 @@ impl<'a> Tarball<'a> {
.unwrap_or("gz");
GeneratedTarball {
- path: crate::dist::distdir(self.builder).join(format!("{package_name}.tar.{ext}")),
+ path: distdir(self.builder).join(format!("{package_name}.tar.{ext}")),
decompressed_output,
work: self.temp_dir,
}
diff --git a/src/ci/docker/host-x86_64/arm-android/Dockerfile b/src/ci/docker/host-x86_64/arm-android/Dockerfile
index db11700af..abca06fb9 100644
--- a/src/ci/docker/host-x86_64/arm-android/Dockerfile
+++ b/src/ci/docker/host-x86_64/arm-android/Dockerfile
@@ -30,7 +30,7 @@ ENV PATH=$PATH:/android/sdk/platform-tools
ENV TARGETS=arm-linux-androideabi
-ENV RUST_CONFIGURE_ARGS --arm-linux-androideabi-ndk=/android/ndk/toolchains/llvm/prebuilt/linux-x86_64/
+ENV RUST_CONFIGURE_ARGS --android-ndk=/android/ndk/
ENV SCRIPT python3 ../x.py --stage 2 test --host='' --target $TARGETS
diff --git a/src/ci/docker/host-x86_64/dist-android/Dockerfile b/src/ci/docker/host-x86_64/dist-android/Dockerfile
index b09b6edb0..20b72b377 100644
--- a/src/ci/docker/host-x86_64/dist-android/Dockerfile
+++ b/src/ci/docker/host-x86_64/dist-android/Dockerfile
@@ -19,12 +19,7 @@ ENV TARGETS=$TARGETS,x86_64-linux-android
ENV RUST_CONFIGURE_ARGS \
--enable-extended \
--enable-profiler \
- --arm-linux-androideabi-ndk=/android/ndk/toolchains/llvm/prebuilt/linux-x86_64/ \
- --armv7-linux-androideabi-ndk=/android/ndk/toolchains/llvm/prebuilt/linux-x86_64/ \
- --thumbv7neon-linux-androideabi-ndk=/android/ndk/toolchains/llvm/prebuilt/linux-x86_64/ \
- --i686-linux-android-ndk=/android/ndk/toolchains/llvm/prebuilt/linux-x86_64/ \
- --aarch64-linux-android-ndk=/android/ndk/toolchains/llvm/prebuilt/linux-x86_64/ \
- --x86_64-linux-android-ndk=/android/ndk/toolchains/llvm/prebuilt/linux-x86_64/ \
+ --android-ndk=/android/ndk/ \
--disable-docs
ENV SCRIPT python3 ../x.py dist --host='' --target $TARGETS
diff --git a/src/ci/docker/host-x86_64/dist-various-1/Dockerfile b/src/ci/docker/host-x86_64/dist-various-1/Dockerfile
index 8f4ad0f4e..3372baed9 100644
--- a/src/ci/docker/host-x86_64/dist-various-1/Dockerfile
+++ b/src/ci/docker/host-x86_64/dist-various-1/Dockerfile
@@ -29,8 +29,6 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
g++-arm-linux-gnueabi \
g++-arm-linux-gnueabihf \
g++-aarch64-linux-gnu \
- g++-mips64-linux-gnuabi64 \
- g++-mips64el-linux-gnuabi64 \
gcc-arm-none-eabi \
gcc-sparc64-linux-gnu \
libc6-dev-sparc64-cross \
@@ -48,12 +46,6 @@ WORKDIR /build
COPY host-x86_64/dist-various-1/install-x86_64-redox.sh /build
RUN ./install-x86_64-redox.sh
-COPY host-x86_64/dist-various-1/install-mips-musl.sh /build
-RUN ./install-mips-musl.sh
-
-COPY host-x86_64/dist-various-1/install-mipsel-musl.sh /build
-RUN ./install-mipsel-musl.sh
-
COPY host-x86_64/dist-various-1/install-aarch64-none-elf.sh /build
RUN ./install-aarch64-none-elf.sh
@@ -76,32 +68,7 @@ RUN env \
env \
CC=arm-linux-gnueabihf-gcc CFLAGS="-march=armv7-a+fp" \
CXX=arm-linux-gnueabihf-g++ CXXFLAGS="-march=armv7-a+fp" \
- bash musl.sh armv7hf && \
- env \
- CC=mips-openwrt-linux-gcc \
- CXX=mips-openwrt-linux-g++ \
- bash musl.sh mips && \
- env \
- CC=mipsel-openwrt-linux-gcc \
- CXX=mipsel-openwrt-linux-g++ \
- bash musl.sh mipsel && \
- env \
- CC=mips64-linux-gnuabi64-gcc \
- CXX=mips64-linux-gnuabi64-g++ \
- bash musl.sh mips64 && \
- env \
- CC=mips64el-linux-gnuabi64-gcc \
- CXX=mips64el-linux-gnuabi64-g++ \
- bash musl.sh mips64el && \
- rm -rf /build/*
-
-# FIXME(mozilla/sccache#235) this shouldn't be necessary but is currently
-# necessary to disambiguate the mips compiler with the mipsel compiler. We want
-# to give these two wrapper scripts (currently identical ones) different hashes
-# to ensure that sccache understands that they're different compilers.
-RUN \
- echo "# a" >> /usr/local/mips-linux-musl/bin/mips-openwrt-linux-musl-wrapper.sh && \
- echo "# b" >> /usr/local/mipsel-linux-musl/bin/mipsel-openwrt-linux-musl-wrapper.sh
+ bash musl.sh armv7hf
ENV RUN_MAKE_TARGETS=thumbv6m-none-eabi
ENV RUN_MAKE_TARGETS=$RUN_MAKE_TARGETS,thumbv7m-none-eabi
@@ -110,10 +77,6 @@ ENV RUN_MAKE_TARGETS=$RUN_MAKE_TARGETS,thumbv7em-none-eabihf
ENV TARGETS=asmjs-unknown-emscripten
ENV TARGETS=$TARGETS,wasm32-unknown-emscripten
-ENV TARGETS=$TARGETS,mips-unknown-linux-musl
-ENV TARGETS=$TARGETS,mipsel-unknown-linux-musl
-ENV TARGETS=$TARGETS,mips64-unknown-linux-muslabi64
-ENV TARGETS=$TARGETS,mips64el-unknown-linux-muslabi64
ENV TARGETS=$TARGETS,arm-unknown-linux-musleabi
ENV TARGETS=$TARGETS,arm-unknown-linux-musleabihf
ENV TARGETS=$TARGETS,armv5te-unknown-linux-gnueabi
@@ -149,10 +112,6 @@ ENV CFLAGS_armv5te_unknown_linux_musleabi="-march=armv5te -marm -mfloat-abi=soft
CFLAGS_arm_unknown_linux_musleabi="-march=armv6 -marm" \
CFLAGS_arm_unknown_linux_musleabihf="-march=armv6 -marm -mfpu=vfp" \
CFLAGS_armv7_unknown_linux_musleabihf="-march=armv7-a+fp" \
- CC_mipsel_unknown_linux_musl=mipsel-openwrt-linux-gcc \
- CC_mips_unknown_linux_musl=mips-openwrt-linux-gcc \
- CC_mips64el_unknown_linux_muslabi64=mips64el-linux-gnuabi64-gcc \
- CC_mips64_unknown_linux_muslabi64=mips64-linux-gnuabi64-gcc \
CC_sparc64_unknown_linux_gnu=sparc64-linux-gnu-gcc \
CC_x86_64_unknown_redox=x86_64-unknown-redox-gcc \
CC_thumbv7neon_unknown_linux_gnueabihf=arm-linux-gnueabihf-gcc \
@@ -177,10 +136,6 @@ ENV RUST_CONFIGURE_ARGS \
--musl-root-arm=/musl-arm \
--musl-root-armhf=/musl-armhf \
--musl-root-armv7hf=/musl-armv7hf \
- --musl-root-mips=/musl-mips \
- --musl-root-mipsel=/musl-mipsel \
- --musl-root-mips64=/musl-mips64 \
- --musl-root-mips64el=/musl-mips64el \
--disable-docs
ENV SCRIPT \
diff --git a/src/ci/docker/host-x86_64/dist-various-1/install-mips-musl.sh b/src/ci/docker/host-x86_64/dist-various-1/install-mips-musl.sh
deleted file mode 100755
index abab18093..000000000
--- a/src/ci/docker/host-x86_64/dist-various-1/install-mips-musl.sh
+++ /dev/null
@@ -1,15 +0,0 @@
-#!/bin/sh
-set -ex
-
-mkdir /usr/local/mips-linux-musl
-
-# originally from
-# https://downloads.openwrt.org/snapshots/trunk/ar71xx/generic/
-# OpenWrt-Toolchain-ar71xx-generic_gcc-5.3.0_musl-1.1.16.Linux-x86_64.tar.bz2
-URL="https://ci-mirrors.rust-lang.org/rustc"
-FILE="OpenWrt-Toolchain-ar71xx-generic_gcc-5.3.0_musl-1.1.16.Linux-x86_64.tar.bz2"
-curl -L "$URL/$FILE" | tar xjf - -C /usr/local/mips-linux-musl --strip-components=2
-
-for file in /usr/local/mips-linux-musl/bin/mips-openwrt-linux-*; do
- ln -s $file /usr/local/bin/`basename $file`
-done
diff --git a/src/ci/docker/host-x86_64/dist-various-1/install-mipsel-musl.sh b/src/ci/docker/host-x86_64/dist-various-1/install-mipsel-musl.sh
deleted file mode 100755
index 779acb2d8..000000000
--- a/src/ci/docker/host-x86_64/dist-various-1/install-mipsel-musl.sh
+++ /dev/null
@@ -1,15 +0,0 @@
-#!/bin/sh
-set -ex
-
-mkdir /usr/local/mipsel-linux-musl
-
-# Note that this originally came from:
-# https://downloads.openwrt.org/snapshots/trunk/malta/generic/
-# OpenWrt-Toolchain-malta-le_gcc-5.3.0_musl-1.1.15.Linux-x86_64.tar.bz2
-URL="https://ci-mirrors.rust-lang.org/rustc"
-FILE="OpenWrt-Toolchain-malta-le_gcc-5.3.0_musl-1.1.15.Linux-x86_64.tar.bz2"
-curl -L "$URL/$FILE" | tar xjf - -C /usr/local/mipsel-linux-musl --strip-components=2
-
-for file in /usr/local/mipsel-linux-musl/bin/mipsel-openwrt-linux-*; do
- ln -s $file /usr/local/bin/`basename $file`
-done
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 6f1b2a6a6..9a2fcb0ce 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
@@ -57,9 +57,9 @@ COPY host-x86_64/dist-x86_64-linux/build-clang.sh /tmp/
RUN ./build-clang.sh
ENV CC=clang CXX=clang++
-# rustc-perf version from 2023-05-30
+# rustc-perf version from 2023-10-22
# Should also be changed in the opt-dist tool for other environments.
-ENV PERF_COMMIT 8b2ac3042e1ff2c0074455a0a3618adef97156b1
+ENV PERF_COMMIT 4f313add609f43e928e98132358e8426ed3969ae
RUN curl -LS -o perf.zip https://ci-mirrors.rust-lang.org/rustc/rustc-perf-$PERF_COMMIT.zip && \
unzip perf.zip && \
mv rustc-perf-$PERF_COMMIT rustc-perf && \
@@ -84,7 +84,8 @@ ENV RUST_CONFIGURE_ARGS \
--set llvm.ninja=false \
--set rust.jemalloc \
--set rust.use-lld=true \
- --set rust.lto=thin
+ --set rust.lto=thin \
+ --set rust.codegen-units=1
ENV SCRIPT python3 ../x.py build --set rust.debug=true opt-dist && \
./build/$HOSTS/stage0-tools-bin/opt-dist linux-ci -- python3 ../x.py dist \
diff --git a/src/ci/docker/host-x86_64/dist-x86_64-linux/build-clang.sh b/src/ci/docker/host-x86_64/dist-x86_64-linux/build-clang.sh
index 02b023fe7..1d9568702 100755
--- a/src/ci/docker/host-x86_64/dist-x86_64-linux/build-clang.sh
+++ b/src/ci/docker/host-x86_64/dist-x86_64-linux/build-clang.sh
@@ -4,7 +4,7 @@ set -ex
source shared.sh
-LLVM=llvmorg-17.0.0-rc3
+LLVM=llvmorg-17.0.4
mkdir llvm-project
cd llvm-project
diff --git a/src/ci/docker/host-x86_64/dist-x86_64-linux/build-gcc.sh b/src/ci/docker/host-x86_64/dist-x86_64-linux/build-gcc.sh
index 6da3f8922..e939a5d7e 100755
--- a/src/ci/docker/host-x86_64/dist-x86_64-linux/build-gcc.sh
+++ b/src/ci/docker/host-x86_64/dist-x86_64-linux/build-gcc.sh
@@ -3,7 +3,8 @@ set -ex
source shared.sh
-GCC=8.5.0
+# Note: in the future when bumping to version 10.1.0, also take care of the sed block below.
+GCC=9.5.0
curl https://ftp.gnu.org/gnu/gcc/gcc-$GCC/gcc-$GCC.tar.xz | xzcat | tar xf -
cd gcc-$GCC
@@ -22,15 +23,25 @@ cd gcc-$GCC
# latter host is presented to `wget`! Therefore, we choose to download from the insecure HTTP server
# instead here.
#
+# Note: in version 10.1.0, the URL used in `download_prerequisites` has changed from using FTP to
+# using HTTP. When bumping to that gcc version, we can likely remove the sed replacement below, or
+# the expression will need to be updated. That new URL is available at:
+# https://github.com/gcc-mirror/gcc/blob/6e6e3f144a33ae504149dc992453b4f6dea12fdb/contrib/download_prerequisites#L35
+#
sed -i'' 's|ftp://gcc\.gnu\.org/|https://gcc.gnu.org/|g' ./contrib/download_prerequisites
./contrib/download_prerequisites
mkdir ../gcc-build
cd ../gcc-build
+
+# '-fno-reorder-blocks-and-partition' is required to
+# enable BOLT optimization of the C++ standard library,
+# which is included in librustc_driver.so
hide_output ../gcc-$GCC/configure \
--prefix=/rustroot \
--enable-languages=c,c++ \
- --disable-gnu-unique-object
+ --disable-gnu-unique-object \
+ --enable-cxx-flags='-fno-reorder-blocks-and-partition'
hide_output make -j$(nproc)
hide_output make install
ln -s gcc /rustroot/bin/cc
diff --git a/src/ci/docker/host-x86_64/test-various/uefi_qemu_test/Cargo.lock b/src/ci/docker/host-x86_64/test-various/uefi_qemu_test/Cargo.lock
new file mode 100644
index 000000000..e983edf20
--- /dev/null
+++ b/src/ci/docker/host-x86_64/test-various/uefi_qemu_test/Cargo.lock
@@ -0,0 +1,16 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "r-efi"
+version = "4.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "575fc2d9b3da54adbdfaddf6eca48fec256d977c8630a1750b8991347d1ac911"
+
+[[package]]
+name = "uefi_qemu_test"
+version = "0.0.0"
+dependencies = [
+ "r-efi",
+]
diff --git a/src/ci/docker/host-x86_64/wasm32/Dockerfile b/src/ci/docker/host-x86_64/wasm32/Dockerfile
deleted file mode 100644
index 0d0f1edd0..000000000
--- a/src/ci/docker/host-x86_64/wasm32/Dockerfile
+++ /dev/null
@@ -1,63 +0,0 @@
-FROM ubuntu:22.04
-
-ARG DEBIAN_FRONTEND=noninteractive
-RUN apt-get update && apt-get install -y --no-install-recommends \
- g++ \
- make \
- ninja-build \
- file \
- curl \
- ca-certificates \
- python3 \
- git \
- cmake \
- sudo \
- gdb \
- xz-utils \
- libssl-dev \
- bzip2 \
- && rm -rf /var/lib/apt/lists/*
-
-COPY scripts/emscripten.sh /scripts/
-RUN bash /scripts/emscripten.sh
-
-COPY scripts/sccache.sh /scripts/
-RUN sh /scripts/sccache.sh
-
-# emcc seems to need python to specifically be "python" and not "python3"
-RUN ln `which python3` /usr/bin/python
-
-ENV PATH=$PATH:/emsdk-portable
-ENV PATH=$PATH:/emsdk-portable/upstream/emscripten/
-
-# Rust's build system requires NodeJS to be in the path, but the directory in
-# which emsdk stores it contains the version number. This caused breakages in
-# the past when emsdk bumped the node version causing the path to point to a
-# missing directory.
-#
-# To avoid the problem this symlinks the latest NodeJs version available to
-# "latest", and adds that to the path.
-RUN ln -s /emsdk-portable/node/$(ls /emsdk-portable/node | sort -V | tail -n 1) \
- /emsdk-portable/node/latest
-ENV PATH=$PATH:/emsdk-portable/node/latest/bin/
-
-ENV BINARYEN_ROOT=/emsdk-portable/upstream/
-ENV EMSDK=/emsdk-portable
-ENV EM_CONFIG=/emsdk-portable/.emscripten
-ENV EM_CACHE=/emsdk-portable/upstream/emscripten/cache
-
-ENV TARGETS=wasm32-unknown-emscripten
-
-# Use -O1 optimizations in the link step to reduce time spent optimizing.
-ENV EMCC_CFLAGS=-O1
-
-COPY static/gitconfig /etc/gitconfig
-
-# Emscripten installation is user-specific
-ENV NO_CHANGE_USER=1
-RUN chown 10719 -R /emsdk-portable/
-
-# Exclude library/alloc due to OOM in benches.
-# FIXME: Fix std tests
-ENV SCRIPT python3 ../x.py test --stage 2 --host='' --target $TARGETS \
- --skip library/alloc --skip library/std
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
index 444e0275d..cefdcad76 100644
--- 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
@@ -24,6 +24,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
xz-utils \
nodejs \
mingw-w64 \
+ libgccjit-12-dev \
&& rm -rf /var/lib/apt/lists/*
# Install powershell (universal package) so we can test x.ps1 on Linux
@@ -34,6 +35,9 @@ RUN curl -sL "https://github.com/PowerShell/PowerShell/releases/download/v7.3.1/
COPY scripts/sccache.sh /scripts/
RUN sh /scripts/sccache.sh
+# Make `libgccjit.so` accessible to the linker.
+RUN ln -s /usr/lib/gcc/x86_64-linux-gnu/12/libgccjit.so /usr/lib/x86_64-linux-gnu/libgccjit.so
+
# 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
@@ -47,6 +51,7 @@ ENV RUST_CONFIGURE_ARGS \
--build=x86_64-unknown-linux-gnu \
--llvm-root=/usr/lib/llvm-15 \
--enable-llvm-link-shared \
+ $USE_NEW_MANGLING \
--set rust.thin-lto-import-instr-limit=10
COPY host-x86_64/x86_64-gnu-llvm-15/script.sh /tmp/
diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-llvm-15/script.sh b/src/ci/docker/host-x86_64/x86_64-gnu-llvm-15/script.sh
index 918b19612..2eb751ca3 100755
--- a/src/ci/docker/host-x86_64/x86_64-gnu-llvm-15/script.sh
+++ b/src/ci/docker/host-x86_64/x86_64-gnu-llvm-15/script.sh
@@ -4,34 +4,46 @@ set -ex
# Only run the stage 1 tests on merges, not on PR CI jobs.
if [[ -z "${PR_CI_JOB}" ]]; then
- ../x.py --stage 1 test --skip 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 && \
- # Run `ui-fulldeps` in `--stage=1`, which actually uses the stage0
- # compiler, and is sensitive to the addition of new flags.
- ../x.py --stage 1 test tests/ui-fulldeps
+ # When running gcc backend tests, we need to install `libgccjit` and to not run llvm codegen
+ # tests as it will fail them.
+ if [[ "${ENABLE_GCC_CODEGEN}" == "1" ]]; then
+ ../x.py --stage 1 test --skip src/tools/tidy --skip tests/codegen
+ else
+ ../x.py --stage 1 test --skip src/tools/tidy
+ fi
+
+ # 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
+
+ # Run `ui-fulldeps` in `--stage=1`, which actually uses the stage0
+ # compiler, and is sensitive to the addition of new flags.
+ ../x.py --stage 1 test tests/ui-fulldeps
fi
+# When running gcc backend tests, we need to install `libgccjit` and to not run llvm codegen
+# tests as it will fail them.
# NOTE: intentionally uses all of `x.py`, `x`, and `x.ps1` to make sure they all work on Linux.
-../x.py --stage 2 test --skip 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
+if [[ "${ENABLE_GCC_CODEGEN}" == "1" ]]; then
+ ../x.py --stage 2 test --skip src/tools/tidy --skip tests/codegen
+else
+ ../x.py --stage 2 test --skip src/tools/tidy
+fi
+
+# 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
diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-llvm-16/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-llvm-16/Dockerfile
index 1e2b802e6..c177e7387 100644
--- a/src/ci/docker/host-x86_64/x86_64-gnu-llvm-16/Dockerfile
+++ b/src/ci/docker/host-x86_64/x86_64-gnu-llvm-16/Dockerfile
@@ -38,6 +38,10 @@ RUN sh /scripts/sccache.sh
# LLVM, rather than the typical src/llvm-project LLVM.
ENV NO_DOWNLOAD_CI_LLVM 1
+# This is not the latest LLVM version, so some components required by tests may
+# be missing.
+ENV IS_NOT_LATEST_LLVM 1
+
# Using llvm-link-shared due to libffi issues -- see #34486
ENV RUST_CONFIGURE_ARGS \
--build=x86_64-unknown-linux-gnu \
diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-llvm-17/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-llvm-17/Dockerfile
new file mode 100644
index 000000000..76846f1fe
--- /dev/null
+++ b/src/ci/docker/host-x86_64/x86_64-gnu-llvm-17/Dockerfile
@@ -0,0 +1,50 @@
+FROM ubuntu:23.10
+
+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-17-tools \
+ llvm-17-dev \
+ libedit-dev \
+ libssl-dev \
+ pkg-config \
+ zlib1g-dev \
+ xz-utils \
+ nodejs \
+ mingw-w64 \
+ && 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-17 \
+ --enable-llvm-link-shared \
+ --set rust.thin-lto-import-instr-limit=10
+
+COPY host-x86_64/x86_64-gnu-llvm-15/script.sh /tmp/
+
+ENV SCRIPT /tmp/script.sh
diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-tools/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-tools/Dockerfile
index 85f2f84a4..82385ea15 100644
--- a/src/ci/docker/host-x86_64/x86_64-gnu-tools/Dockerfile
+++ b/src/ci/docker/host-x86_64/x86_64-gnu-tools/Dockerfile
@@ -15,6 +15,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
sudo \
xz-utils \
tidy \
+ libgccjit-12-dev \
\
# Install dependencies for chromium browser
gconf-service \
@@ -61,6 +62,11 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
COPY scripts/sccache.sh /scripts/
RUN sh /scripts/sccache.sh
+# Make `libgccjit.so` accessible.
+RUN ln -s /usr/lib/gcc/x86_64-linux-gnu/12/libgccjit.so /usr/lib/x86_64-linux-gnu/libgccjit.so
+# Fix rustc_codegen_gcc lto issues.
+ENV GCC_EXEC_PREFIX="/usr/lib/gcc/"
+
COPY host-x86_64/x86_64-gnu-tools/checktools.sh /tmp/
RUN curl -sL https://nodejs.org/dist/v14.20.0/node-v14.20.0-linux-x64.tar.xz | tar -xJ
@@ -81,7 +87,10 @@ RUN npm install -g browser-ui-test@$(head -n 1 /tmp/browser-ui-test.version) --u
ENV RUST_CONFIGURE_ARGS \
--build=x86_64-unknown-linux-gnu \
- --save-toolstates=/tmp/toolstate/toolstates.json
+ --save-toolstates=/tmp/toolstate/toolstates.json \
+ --enable-new-symbol-mangling
+
+ENV HOST_TARGET x86_64-unknown-linux-gnu
ENV SCRIPT /tmp/checktools.sh ../x.py && \
NODE_PATH=`npm root -g` python3 ../x.py test tests/rustdoc-gui --stage 2 \
diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-tools/checktools.sh b/src/ci/docker/host-x86_64/x86_64-gnu-tools/checktools.sh
index 7dde63709..821a09feb 100755
--- a/src/ci/docker/host-x86_64/x86_64-gnu-tools/checktools.sh
+++ b/src/ci/docker/host-x86_64/x86_64-gnu-tools/checktools.sh
@@ -1,4 +1,5 @@
#!/bin/sh
+# ignore-tidy-linelength
set -eu
@@ -26,8 +27,30 @@ python3 "$X_PY" test --stage 2 src/tools/clippy
python3 "$X_PY" test --stage 2 src/tools/rustfmt
python3 "$X_PY" test --stage 2 src/tools/miri
# We natively run this script on x86_64-unknown-linux-gnu and x86_64-pc-windows-msvc.
-# Also cover some other targets (on both of these hosts) via cross-testing.
+# Also cover some other targets via cross-testing, in particular all tier 1 targets.
export BOOTSTRAP_SKIP_TARGET_SANITY=1 # we don't need `cc` for these targets
-python3 "$X_PY" test --stage 2 src/tools/miri --target i686-pc-windows-msvc
-python3 "$X_PY" test --stage 2 src/tools/miri --target aarch64-apple-darwin
+case $HOST_TARGET in
+ x86_64-unknown-linux-gnu)
+ # Only this branch runs in PR CI.
+ # Fully test all main OSes, including a 32bit target.
+ python3 "$X_PY" test --stage 2 src/tools/miri --target x86_64-apple-darwin
+ python3 "$X_PY" test --stage 2 src/tools/miri --target i686-pc-windows-msvc
+ # Only run "pass" tests for the remaining targets, which is quite a bit faster.
+ python3 "$X_PY" test --stage 2 src/tools/miri --target x86_64-pc-windows-gnu --test-args pass
+ python3 "$X_PY" test --stage 2 src/tools/miri --target i686-unknown-linux-gnu --test-args pass
+ python3 "$X_PY" test --stage 2 src/tools/miri --target aarch64-unknown-linux-gnu --test-args pass
+ python3 "$X_PY" test --stage 2 src/tools/miri --target s390x-unknown-linux-gnu --test-args pass
+ ;;
+ x86_64-pc-windows-msvc)
+ # Strangely, Linux targets do not work here. cargo always says
+ # "error: cannot produce cdylib for ... as the target ... does not support these crate types".
+ # Only run "pass" tests, which is quite a bit faster.
+ python3 "$X_PY" test --stage 2 src/tools/miri --target aarch64-apple-darwin --test-args pass
+ python3 "$X_PY" test --stage 2 src/tools/miri --target i686-pc-windows-gnu --test-args pass
+ ;;
+ *)
+ echo "FATAL: unexpected host $HOST_TARGET"
+ exit 1
+ ;;
+esac
unset BOOTSTRAP_SKIP_TARGET_SANITY
diff --git a/src/ci/docker/run.sh b/src/ci/docker/run.sh
index e9c155b13..cedbc0390 100755
--- a/src/ci/docker/run.sh
+++ b/src/ci/docker/run.sh
@@ -235,7 +235,7 @@ else
args="$args --volume /tmp/toolstate:/tmp/toolstate"
id=$(id -u)
- if [[ "$id" != 0 && "$(docker -v)" =~ ^podman ]]; then
+ if [[ "$id" != 0 && "$(docker version)" =~ Podman ]]; then
# Rootless podman creates a separate user namespace, where an inner
# LOCAL_USER_ID will map to a different subuid range on the host.
# The "keep-id" mode maps the current UID directly into the container.
@@ -264,10 +264,27 @@ else
BASE_COMMIT=""
fi
+SUMMARY_FILE=github-summary.md
+touch $objdir/${SUMMARY_FILE}
+
+extra_env=""
+if [ "$ENABLE_GCC_CODEGEN" = "1" ]; then
+ extra_env="$EXTRA_ENV --env ENABLE_GCC_CODEGEN=1"
+ # If `ENABLE_GCC_CODEGEN` is set and not empty, we add the `--enable-new-symbol-mangling`
+ # argument to `RUST_CONFIGURE_ARGS` and set the `GCC_EXEC_PREFIX` environment variable.
+ # `cg_gcc` doesn't support the legacy mangling so we need to enforce the new one
+ # if we run `cg_gcc` tests.
+ extra_env="$EXTRA_ENV --env USE_NEW_MANGLING=--enable-new-symbol-mangling"
+ # Fix rustc_codegen_gcc lto issues.
+ extra_env="$EXTRA_ENV --env GCC_EXEC_PREFIX=/usr/lib/gcc/"
+ echo "Setting extra environment values for docker: $extra_env"
+fi
+
docker \
run \
--workdir /checkout/obj \
--env SRC=/checkout \
+ $extra_env \
$args \
--env CARGO_HOME=/cargo \
--env DEPLOY \
@@ -275,6 +292,7 @@ docker \
--env CI \
--env GITHUB_ACTIONS \
--env GITHUB_REF \
+ --env GITHUB_STEP_SUMMARY="/checkout/obj/${SUMMARY_FILE}" \
--env TOOLSTATE_REPO_ACCESS_TOKEN \
--env TOOLSTATE_REPO \
--env TOOLSTATE_PUBLISH \
@@ -284,11 +302,14 @@ docker \
--env DIST_TRY_BUILD \
--env PR_CI_JOB \
--env OBJDIR_ON_HOST="$objdir" \
+ --env CODEGEN_BACKENDS \
--init \
--rm \
rust-ci \
$command
+cat $objdir/${SUMMARY_FILE} >> "${GITHUB_STEP_SUMMARY}"
+
if [ -f /.dockerenv ]; then
rm -rf $objdir
docker cp checkout:/checkout/obj $objdir
diff --git a/src/ci/docker/scripts/fuchsia-test-runner.py b/src/ci/docker/scripts/fuchsia-test-runner.py
index f78d446c8..437b51641 100755
--- a/src/ci/docker/scripts/fuchsia-test-runner.py
+++ b/src/ci/docker/scripts/fuchsia-test-runner.py
@@ -12,6 +12,7 @@ from dataclasses import dataclass
import fcntl
import glob
import hashlib
+import io
import json
import os
import platform
@@ -276,27 +277,60 @@ class TestEnvironment:
stderr=self.subprocess_output(),
)
- # Start emulator
- self.log_info("Starting emulator...")
- product_bundle = "terminal.qemu-" + self.triple_to_arch(self.target)
+ # Look up the product bundle transfer manifest.
+ self.log_info("Looking up the product bundle transfer manifest...")
+ product_name = "minimal." + self.triple_to_arch(self.target)
+ fuchsia_version = "14.20230811.2.1"
+
+ # FIXME: We should be able to replace this with the machine parsable
+ # `ffx --machine json product lookup ...` once F15 is released.
+ out = subprocess.check_output(
+ [
+ ffx_path,
+ "product",
+ "lookup",
+ product_name,
+ fuchsia_version,
+ "--base-url",
+ "gs://fuchsia/development/" + fuchsia_version,
+ ],
+ env=ffx_env,
+ stderr=self.subprocess_output(),
+ )
+
+ self.log_debug(out)
+
+ for line in io.BytesIO(out):
+ if line.startswith(b"gs://"):
+ transfer_manifest_url = line.rstrip()
+ break
+ else:
+ raise Exception("Unable to parse transfer manifest")
+
+ # Download the product bundle.
+ product_bundle_dir = os.path.join(self.tmp_dir(), 'product-bundle')
subprocess.check_call(
[
ffx_path,
- "product-bundle",
- "get",
- product_bundle,
+ "product",
+ "download",
+ transfer_manifest_url,
+ product_bundle_dir,
+ "--force",
],
env=ffx_env,
stdout=self.subprocess_output(),
stderr=self.subprocess_output(),
)
+
+ # Start emulator
# FIXME: condition --accel hyper on target arch matching host arch
subprocess.check_call(
[
ffx_path,
"emu",
"start",
- product_bundle,
+ product_bundle_dir,
"--headless",
"--log",
self.emulator_log_path(),
diff --git a/src/ci/github-actions/ci.yml b/src/ci/github-actions/ci.yml
index 858ebf72a..da29ffb8e 100644
--- a/src/ci/github-actions/ci.yml
+++ b/src/ci/github-actions/ci.yml
@@ -91,6 +91,10 @@ x--expand-yaml-anchors--remove:
os: macos-13 # We use the standard runner for now
<<: *base-job
+ - &job-macos-m1
+ os: macos-13-xlarge
+ <<: *base-job
+
- &job-windows-8c
os: windows-2019-8core-32gb
<<: *base-job
@@ -153,6 +157,10 @@ x--expand-yaml-anchors--remove:
run: src/ci/scripts/dump-environment.sh
<<: *step
+ - name: install awscli
+ run: src/ci/scripts/install-awscli.sh
+ <<: *step
+
- name: install sccache
run: src/ci/scripts/install-sccache.sh
<<: *step
@@ -165,6 +173,10 @@ x--expand-yaml-anchors--remove:
run: src/ci/scripts/install-clang.sh
<<: *step
+ - name: install tidy
+ run: src/ci/scripts/install-tidy.sh
+ <<: *step
+
- name: install WIX
run: src/ci/scripts/install-wix.sh
<<: *step
@@ -281,6 +293,7 @@ on:
- auto
- try
- try-perf
+ - automation/bors/try
- master
pull_request:
branches:
@@ -322,6 +335,8 @@ jobs:
<<: *job-linux-4c
- name: x86_64-gnu-llvm-15
+ env:
+ ENABLE_GCC_CODEGEN: "1"
<<: *job-linux-16c
- name: x86_64-gnu-tools
@@ -350,6 +365,8 @@ jobs:
<<: *job-linux-8c
- name: dist-aarch64-linux
+ env:
+ CODEGEN_BACKENDS: llvm,cranelift
<<: *job-linux-8c
- name: dist-android
@@ -402,14 +419,19 @@ jobs:
- &dist-x86_64-linux
name: dist-x86_64-linux
+ env:
+ CODEGEN_BACKENDS: llvm,cranelift
<<: *job-linux-16c
- name: dist-x86_64-linux-alt
env:
IMAGE: dist-x86_64-linux
+ CODEGEN_BACKENDS: llvm,cranelift
<<: *job-linux-16c
- name: dist-x86_64-musl
+ env:
+ CODEGEN_BACKENDS: llvm,cranelift
<<: *job-linux-8c
- name: dist-x86_64-netbsd
@@ -427,20 +449,6 @@ jobs:
- name: test-various
<<: *job-linux-8c
- - name: wasm32
- env:
- # Running emscripten tests currently requires that we are
- # building a nightly toolchain. Otherwise, we cannot pass
- # -Zunstable-options to libtest. Normally we workaround this by
- # setting RUSTC_BOOTSTRAP in the environment, but that doesn't
- # work for emscripten as environment variables are not threaded
- # into the compiled code.
- #
- # For more details see:
- # https://emscripten.org/docs/porting/connecting_cpp_and_javascript/Interacting-with-code.html#environment-variables
- RUST_CI_OVERRIDE_RELEASE_CHANNEL: nightly
- <<: *job-linux-8c
-
- name: x86_64-gnu
<<: *job-linux-4c
@@ -468,6 +476,11 @@ jobs:
- name: x86_64-gnu-distcheck
<<: *job-linux-8c
+ - name: x86_64-gnu-llvm-17
+ env:
+ RUST_BACKTRACE: 1
+ <<: *job-linux-8c
+
- name: x86_64-gnu-llvm-16
env:
RUST_BACKTRACE: 1
@@ -501,6 +514,7 @@ jobs:
NO_DEBUG_ASSERTIONS: 1
NO_OVERFLOW_CHECKS: 1
DIST_REQUIRE_ALL_TOOLS: 1
+ CODEGEN_BACKENDS: llvm,cranelift
<<: *job-macos-xl
- name: dist-apple-various
@@ -536,17 +550,14 @@ jobs:
# This target only needs to support 11.0 and up as nothing else supports the hardware
- name: dist-aarch64-apple
env:
- SCRIPT: ./x.py dist bootstrap --include-default-paths --stage 2
+ SCRIPT: ./x.py dist bootstrap --include-default-paths --host=aarch64-apple-darwin --target=aarch64-apple-darwin
RUST_CONFIGURE_ARGS: >-
- --build=x86_64-apple-darwin
- --host=aarch64-apple-darwin
- --target=aarch64-apple-darwin
--enable-full-tools
--enable-sanitizers
--enable-profiler
- --disable-docs
--set rust.jemalloc
--set llvm.ninja=false
+ --set rust.lto=thin
RUSTC_RETRY_LINKER_ON_SEGFAULT: 1
SELECT_XCODE: /Applications/Xcode_13.4.1.app
USE_XCODE_CLANG: 1
@@ -556,15 +567,26 @@ jobs:
NO_DEBUG_ASSERTIONS: 1
NO_OVERFLOW_CHECKS: 1
DIST_REQUIRE_ALL_TOOLS: 1
- # Corresponds to 16K page size
- #
- # Shouldn't be needed if jemalloc-sys is updated to
- # handle this platform like iOS or if we build on
- # aarch64-apple-darwin itself.
- #
- # https://github.com/gnzlbg/jemallocator/blob/c27a859e98e3cb790dc269773d9da71a1e918458/jemalloc-sys/build.rs#L237
- JEMALLOC_SYS_WITH_LG_PAGE: 14
- <<: *job-macos-xl
+ <<: *job-macos-m1
+
+ # This target only needs to support 11.0 and up as nothing else supports the hardware
+ - name: aarch64-apple
+ env:
+ SCRIPT: ./x.py --stage 2 test --host=aarch64-apple-darwin --target=aarch64-apple-darwin
+ RUST_CONFIGURE_ARGS: >-
+ --enable-sanitizers
+ --enable-profiler
+ --set rust.jemalloc
+ --set llvm.ninja=false
+ RUSTC_RETRY_LINKER_ON_SEGFAULT: 1
+ SELECT_XCODE: /Applications/Xcode_13.4.1.app
+ USE_XCODE_CLANG: 1
+ MACOSX_DEPLOYMENT_TARGET: 11.0
+ MACOSX_STD_DEPLOYMENT_TARGET: 11.0
+ NO_LLVM_ASSERTIONS: 1
+ NO_DEBUG_ASSERTIONS: 1
+ NO_OVERFLOW_CHECKS: 1
+ <<: *job-macos-m1
######################
# Windows Builders #
@@ -585,6 +607,7 @@ jobs:
- name: x86_64-msvc-ext
env:
SCRIPT: python x.py --stage 2 test src/tools/cargotest src/tools/cargo && src/ci/docker/host-x86_64/x86_64-gnu-tools/checktools.sh x.py /tmp/toolstate/toolstates.json windows
+ HOST_TARGET: x86_64-pc-windows-msvc
RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-msvc --enable-lld --save-toolstates=/tmp/toolstate/toolstates.json
DEPLOY_TOOLSTATES_JSON: toolstates-windows.json
<<: *job-windows-8c
@@ -702,12 +725,14 @@ jobs:
env:
DIST_TRY_BUILD: 1
<<: [*shared-ci-variables, *prod-variables]
- if: github.event_name == 'push' && (github.ref == 'refs/heads/try' || github.ref == 'refs/heads/try-perf') && github.repository == 'rust-lang-ci/rust'
+ if: github.event_name == 'push' && (((github.ref == 'refs/heads/try' || github.ref == 'refs/heads/try-perf') && github.repository == 'rust-lang-ci/rust') || ((github.ref == 'refs/heads/automation/bors/try') && github.repository == 'rust-lang/rust'))
strategy:
matrix:
include:
- &dist-x86_64-linux
name: dist-x86_64-linux
+ env:
+ CODEGEN_BACKENDS: llvm,cranelift
<<: *job-linux-16c
master:
diff --git a/src/ci/run.sh b/src/ci/run.sh
index 98f2cdac5..ce0dd6018 100755
--- a/src/ci/run.sh
+++ b/src/ci/run.sh
@@ -47,7 +47,8 @@ source "$ci_dir/shared.sh"
export CARGO_REGISTRIES_CRATES_IO_PROTOCOL=sparse
-if ! isCI || isCiBranch auto || isCiBranch beta || isCiBranch try || isCiBranch try-perf; then
+if ! isCI || isCiBranch auto || isCiBranch beta || isCiBranch try || isCiBranch try-perf || \
+ isCiBranch automation/bors/try; then
RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --set build.print-step-timings --enable-verbose-tests"
RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --set build.metrics"
HAS_METRICS=1
@@ -97,12 +98,14 @@ if [ "$DEPLOY$DEPLOY_ALT" = "1" ]; then
if [ "$NO_LLVM_ASSERTIONS" = "1" ]; then
RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --disable-llvm-assertions"
elif [ "$DEPLOY_ALT" != "" ]; then
- if [ "$NO_PARALLEL_COMPILER" = "" ]; then
- RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --set rust.parallel-compiler"
+ if [ "$ALT_PARALLEL_COMPILER" = "" ]; then
+ RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --set rust.parallel-compiler=false"
fi
RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --enable-llvm-assertions"
RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --set rust.verify-llvm-ir"
fi
+
+ RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --set rust.codegen-backends=${CODEGEN_BACKENDS:-llvm}"
else
# We almost always want debug assertions enabled, but sometimes this takes too
# long for too little benefit, so we just turn them off.
@@ -123,8 +126,15 @@ else
RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --set rust.verify-llvm-ir"
- # Test the Cranelift backend in on CI, but don't ship it.
- RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --set rust.codegen-backends=llvm,cranelift"
+ # When running gcc backend tests, we need to install `libgccjit` and to not run llvm codegen
+ # tests as it will fail them.
+ if [[ "${ENABLE_GCC_CODEGEN}" == "1" ]]; then
+ # Test the Cranelift and GCC backends in CI. Bootstrap knows which targets to run tests on.
+ RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --set rust.codegen-backends=llvm,cranelift,gcc"
+ else
+ # Test the Cranelift backend in CI. Bootstrap knows which targets to run tests on.
+ RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --set rust.codegen-backends=llvm,cranelift"
+ fi
# We enable this for non-dist builders, since those aren't trying to produce
# fresh binaries. We currently don't entirely support distributing a fresh
diff --git a/src/ci/scripts/install-awscli.sh b/src/ci/scripts/install-awscli.sh
new file mode 100755
index 000000000..b4a239fd3
--- /dev/null
+++ b/src/ci/scripts/install-awscli.sh
@@ -0,0 +1,29 @@
+#!/bin/bash
+# This script downloads and installs the awscli binaries directly from
+# Amazon.
+
+set -euo pipefail
+IFS=$'\n\t'
+
+source "$(cd "$(dirname "$0")" && pwd)/../shared.sh"
+
+AWS_VERSION="2.13.25"
+
+# Only the macOS arm64/aarch64 GitHub Actions runner needs to have AWS
+# installed; other platforms have it preinstalled.
+
+if isMacOS; then
+ platform=$(uname -m)
+ case $platform in
+ x86_64)
+ ;;
+ arm64)
+ file="https://awscli.amazonaws.com/AWSCLIV2-${AWS_VERSION}.pkg"
+ retry curl -f "${file}" -o "AWSCLIV2.pkg"
+ sudo installer -pkg "AWSCLIV2.pkg" -target /
+ ;;
+ *)
+ echo "unsupported architecture: ${platform}"
+ exit 1
+ esac
+fi
diff --git a/src/ci/scripts/install-tidy.sh b/src/ci/scripts/install-tidy.sh
new file mode 100755
index 000000000..fab126453
--- /dev/null
+++ b/src/ci/scripts/install-tidy.sh
@@ -0,0 +1,24 @@
+#!/bin/bash
+# This script downloads and installs the tidy binary from Homebrew.
+
+set -euo pipefail
+IFS=$'\n\t'
+
+source "$(cd "$(dirname "$0")" && pwd)/../shared.sh"
+
+# Only the macOS arm64/aarch64 GitHub Actions runner needs to have tidy
+# installed; other platforms have it preinstalled.
+
+if isMacOS; then
+ platform=$(uname -m)
+ case $platform in
+ x86_64)
+ ;;
+ arm64)
+ brew install tidy-html5
+ ;;
+ *)
+ echo "unsupported architecture: ${platform}"
+ exit 1
+ esac
+fi
diff --git a/src/ci/scripts/verify-channel.sh b/src/ci/scripts/verify-channel.sh
index cd28748a4..edeea2014 100755
--- a/src/ci/scripts/verify-channel.sh
+++ b/src/ci/scripts/verify-channel.sh
@@ -8,7 +8,7 @@ IFS=$'\n\t'
source "$(cd "$(dirname "$0")" && pwd)/../shared.sh"
-if isCiBranch auto || isCiBranch try || isCiBranch try-perf; then
+if isCiBranch auto || isCiBranch try || isCiBranch try-perf || isCiBranch automation/bors/try; then
echo "channel verification is only executed on PR builds"
exit
fi
diff --git a/src/doc/book/redirects/compiler-plugins.md b/src/doc/book/redirects/compiler-plugins.md
index 66061adf5..67187f5f6 100644
--- a/src/doc/book/redirects/compiler-plugins.md
+++ b/src/doc/book/redirects/compiler-plugins.md
@@ -2,12 +2,5 @@
<small>There is a new edition of the book and this is an old link.</small>
-> Compiler plugins are user-provided libraries that extend the compiler's behavior with new syntax extensions, lint checks, etc.
-
----
-
-This particular chapter has moved to [the Unstable Book][2].
-
-* **[In the Unstable Rust Book: `plugin`][2]**
-
-[2]: ../unstable-book/language-features/plugin.html
+> Compiler plugins were user-provided libraries that extended the compiler's behavior in certain ways.
+> Support for them has been removed.
diff --git a/src/doc/book/src/ch02-00-guessing-game-tutorial.md b/src/doc/book/src/ch02-00-guessing-game-tutorial.md
index 4f2857333..5e27fb114 100644
--- a/src/doc/book/src/ch02-00-guessing-game-tutorial.md
+++ b/src/doc/book/src/ch02-00-guessing-game-tutorial.md
@@ -930,8 +930,8 @@ discusses structs and method syntax, and Chapter 6 explains how enums work.
[randcrate]: https://crates.io/crates/rand
[semver]: http://semver.org
[cratesio]: https://crates.io/
-[doccargo]: http://doc.crates.io
-[doccratesio]: http://doc.crates.io/crates-io.html
+[doccargo]: https://doc.rust-lang.org/cargo/
+[doccratesio]: https://doc.rust-lang.org/cargo/reference/publishing.html
[match]: ch06-02-match.html
[shadowing]: ch03-01-variables-and-mutability.html#shadowing
[parse]: ../std/primitive.str.html#method.parse
diff --git a/src/doc/embedded-book/src/start/hardware.md b/src/doc/embedded-book/src/start/hardware.md
index 8166e62e5..ba9fe6e76 100644
--- a/src/doc/embedded-book/src/start/hardware.md
+++ b/src/doc/embedded-book/src/start/hardware.md
@@ -64,6 +64,9 @@ target = "thumbv7em-none-eabihf" # Cortex-M4F and Cortex-M7F (with FPU)
```
We'll use `thumbv7em-none-eabihf` as that covers the Cortex-M4F core.
+> **NOTE**: As you may remember from the previous chapter, we have to install
+> all targets and this is a new one. So don't forget to run the installation
+> process `rustup target add thumbv7em-none-eabihf` for this target.
The second step is to enter the memory region information into the `memory.x`
file.
diff --git a/src/doc/guide-plugins.md b/src/doc/guide-plugins.md
index 6c5115487..ccb9bb2ee 100644
--- a/src/doc/guide-plugins.md
+++ b/src/doc/guide-plugins.md
@@ -1,4 +1,3 @@
% The (old) Rust Compiler Plugins Guide
-This content has moved into
-[the Unstable Book](unstable-book/language-features/plugin.html).
+Support for plugins has been removed.
diff --git a/src/doc/nomicon/src/exception-safety.md b/src/doc/nomicon/src/exception-safety.md
index 8404bb859..762b38b98 100644
--- a/src/doc/nomicon/src/exception-safety.md
+++ b/src/doc/nomicon/src/exception-safety.md
@@ -172,7 +172,7 @@ impl<'a, T> Hole<'a, T> {
fn removed(&self) -> &T { self.elt.as_ref().unwrap() }
- unsafe fn get(&self, index: usize) -> &T { &self.data[index] }
+ fn get(&self, index: usize) -> &T { &self.data[index] }
unsafe fn move_to(&mut self, index: usize) {
let index_ptr: *const _ = &self.data[index];
diff --git a/src/doc/reference/src/attributes.md b/src/doc/reference/src/attributes.md
index 92ce1cd09..a1ad5c60c 100644
--- a/src/doc/reference/src/attributes.md
+++ b/src/doc/reference/src/attributes.md
@@ -275,8 +275,8 @@ The following is an index of all built-in attributes.
- [`debugger_visualizer`] — Embeds a file that specifies debugger output for a type.
[Doc comments]: comments.md#doc-comments
-[ECMA-334]: https://www.ecma-international.org/publications/standards/Ecma-334.htm
-[ECMA-335]: https://www.ecma-international.org/publications/standards/Ecma-335.htm
+[ECMA-334]: https://www.ecma-international.org/publications-and-standards/standards/ecma-334/
+[ECMA-335]: https://www.ecma-international.org/publications-and-standards/standards/ecma-335/
[Expression Attributes]: expressions.md#expression-attributes
[IDENTIFIER]: identifiers.md
[RAW_STRING_LITERAL]: tokens.md#raw-string-literals
diff --git a/src/doc/reference/src/attributes/codegen.md b/src/doc/reference/src/attributes/codegen.md
index c929f979c..7a50fe26f 100644
--- a/src/doc/reference/src/attributes/codegen.md
+++ b/src/doc/reference/src/attributes/codegen.md
@@ -204,6 +204,66 @@ Feature | Implicitly Enables | Feature Name
`tme` | | FEAT_TME - Transactional Memory Extension
`vh` | | FEAT_VHE - Virtualization Host Extensions
+#### `riscv32` or `riscv64`
+
+This platform requires that `#[target_feature]` is only applied to [`unsafe`
+functions][unsafe function].
+
+Further documentation on these features can be found in their respective
+specification. Many specifications are described in the [RISC-V ISA Manual] or
+in another manual hosted on the [RISC-V GitHub Account].
+
+[RISC-V ISA Manual]: https://github.com/riscv/riscv-isa-manual
+[RISC-V GitHub Account]: https://github.com/riscv
+
+Feature | Implicitly Enables | Description
+------------|---------------------|-------------------
+`a` | | [A][rv-a] — Atomic instructions
+`c` | | [C][rv-c] — Compressed instructions
+`m` | | [M][rv-m] — Integer Multiplication and Division instructions
+`zb` | `zba`, `zbc`, `zbs` | [Zb][rv-zb] — Bit Manipulation instructions
+`zba` | | [Zba][rv-zb-zba] — Address Generation instructions
+`zbb` | | [Zbb][rv-zb-zbb] — Basic bit-manipulation
+`zbc` | | [Zbc][rv-zb-zbc] — Carry-less multiplication
+`zbkb` | | [Zbkb][rv-zb-zbkb] — Bit Manipulation Instructions for Cryptography
+`zbkc` | | [Zbkc][rv-zb-zbc] — Carry-less multiplication for Cryptography
+`zbkx` | | [Zbkx][rv-zb-zbkx] — Crossbar permutations
+`zbs` | | [Zbs][rv-zb-zbs] — Single-bit instructions
+`zk` | `zkn`, `zkr`, `zks`, `zkt`, `zbkb`, `zbkc`, `zkbx` | [Zk][rv-zk] — Scalar Cryptography
+`zkn` | `zknd`, `zkne`, `zknh`, `zbkb`, `zbkc`, `zkbx` | [Zkn][rv-zkn] — NIST Algorithm suite extension
+`zknd` | | [Zknd][rv-zknd] — NIST Suite: AES Decryption
+`zkne` | | [Zkne][rv-zkne] — NIST Suite: AES Encryption
+`zknh` | | [Zknh][rv-zknh] — NIST Suite: Hash Function Instructions
+`zkr` | | [Zkr][rv-zkr] — Entropy Source Extension
+`zks` | `zksed`, `zksh`, `zbkb`, `zbkc`, `zkbx` | [Zks][rv-zks] — ShangMi Algorithm Suite
+`zksed` | | [Zksed][rv-zksed] — ShangMi Suite: SM4 Block Cipher Instructions
+`zksh` | | [Zksh][rv-zksh] — ShangMi Suite: SM3 Hash Function Instructions
+`zkt` | | [Zkt][rv-zkt] — Data Independent Execution Latency Subset
+
+<!-- Keep links near each table to make it easier to move and update. -->
+
+[rv-a]: https://github.com/riscv/riscv-isa-manual/blob/de46343a245c6ee1f7b1a40c92fe1a86bd4f4978/src/a-st-ext.adoc
+[rv-c]: https://github.com/riscv/riscv-isa-manual/blob/de46343a245c6ee1f7b1a40c92fe1a86bd4f4978/src/c-st-ext.adoc
+[rv-m]: https://github.com/riscv/riscv-isa-manual/blob/de46343a245c6ee1f7b1a40c92fe1a86bd4f4978/src/m-st-ext.adoc
+[rv-zb]: https://github.com/riscv/riscv-bitmanip
+[rv-zb-zba]: https://github.com/riscv/riscv-bitmanip/blob/main/bitmanip/zba.adoc
+[rv-zb-zbb]: https://github.com/riscv/riscv-bitmanip/blob/main/bitmanip/zbb.adoc
+[rv-zb-zbc]: https://github.com/riscv/riscv-bitmanip/blob/main/bitmanip/zbc.adoc
+[rv-zb-zbkb]: https://github.com/riscv/riscv-bitmanip/blob/main/bitmanip/zbkb.adoc
+[rv-zb-zbkc]: https://github.com/riscv/riscv-bitmanip/blob/main/bitmanip/zbkc.adoc
+[rv-zb-zbkx]: https://github.com/riscv/riscv-bitmanip/blob/main/bitmanip/zbkx.adoc
+[rv-zb-zbs]: https://github.com/riscv/riscv-bitmanip/blob/main/bitmanip/zbs.adoc
+[rv-zk]: https://github.com/riscv/riscv-crypto/blob/e2dd7d98b7f34d477e38cb5fd7a3af4379525189/doc/scalar/riscv-crypto-scalar-zk.adoc
+[rv-zkn]: https://github.com/riscv/riscv-crypto/blob/e2dd7d98b7f34d477e38cb5fd7a3af4379525189/doc/scalar/riscv-crypto-scalar-zkn.adoc
+[rv-zkne]: https://github.com/riscv/riscv-crypto/blob/e2dd7d98b7f34d477e38cb5fd7a3af4379525189/doc/scalar/riscv-crypto-scalar-zkne.adoc
+[rv-zknd]: https://github.com/riscv/riscv-crypto/blob/e2dd7d98b7f34d477e38cb5fd7a3af4379525189/doc/scalar/riscv-crypto-scalar-zknd.adoc
+[rv-zknh]: https://github.com/riscv/riscv-crypto/blob/e2dd7d98b7f34d477e38cb5fd7a3af4379525189/doc/scalar/riscv-crypto-scalar-zknh.adoc
+[rv-zkr]: https://github.com/riscv/riscv-crypto/blob/e2dd7d98b7f34d477e38cb5fd7a3af4379525189/doc/scalar/riscv-crypto-scalar-zkr.adoc
+[rv-zks]: https://github.com/riscv/riscv-crypto/blob/e2dd7d98b7f34d477e38cb5fd7a3af4379525189/doc/scalar/riscv-crypto-scalar-zks.adoc
+[rv-zksed]: https://github.com/riscv/riscv-crypto/blob/e2dd7d98b7f34d477e38cb5fd7a3af4379525189/doc/scalar/riscv-crypto-scalar-zksed.adoc
+[rv-zksh]: https://github.com/riscv/riscv-crypto/blob/e2dd7d98b7f34d477e38cb5fd7a3af4379525189/doc/scalar/riscv-crypto-scalar-zksh.adoc
+[rv-zkt]: https://github.com/riscv/riscv-crypto/blob/e2dd7d98b7f34d477e38cb5fd7a3af4379525189/doc/scalar/riscv-crypto-scalar-zkr.adoc
+
#### `wasm32` or `wasm64`
`#[target_feature]` may be used with both safe and
diff --git a/src/doc/reference/src/behavior-considered-undefined.md b/src/doc/reference/src/behavior-considered-undefined.md
index f140fb20c..756b86db0 100644
--- a/src/doc/reference/src/behavior-considered-undefined.md
+++ b/src/doc/reference/src/behavior-considered-undefined.md
@@ -27,9 +27,12 @@ Please read the [Rustonomicon] before writing unsafe code.
</div>
* 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)`).
+* Accessing (loading from or storing to) a place that is [dangling] or [based on
+ a misaligned pointer].
+* Performing a place projection that violates the requirements of [in-bounds
+ pointer arithmetic][offset]. A place projection is a [field
+ expression][project-field], a [tuple index expression][project-tuple], or an
+ [array/slice index expression][project-slice].
* Breaking the [pointer aliasing rules]. `Box<T>`, `&mut T` and `&T` follow
LLVM’s scoped [noalias] model, except if the `&T` contains an
[`UnsafeCell<U>`]. References and boxes must not be [dangling] while they are
@@ -68,7 +71,7 @@ Please read the [Rustonomicon] before writing unsafe code.
* A `!` (all values are invalid for this type).
* An integer (`i*`/`u*`), floating point value (`f*`), or raw pointer obtained
from [uninitialized memory][undef], or uninitialized memory in a `str`.
- * A reference or `Box<T>` that is [dangling], unaligned, or points to an invalid value.
+ * A reference or `Box<T>` that is [dangling], misaligned, or points to an invalid value.
* Invalid metadata in a wide reference, `Box<T>`, or raw pointer:
* `dyn Trait` metadata is invalid if it is not a pointer to a vtable for
`Trait` that matches the actual dynamic trait the pointer or reference points to.
@@ -102,6 +105,36 @@ reading uninitialized memory is permitted are inside `union`s and in "padding"
The span of bytes a pointer or reference "points to" is determined by the pointer value and the size of the pointee type (using `size_of_val`).
+### Places based on misaligned pointers
+[based on a misaligned pointer]: #places-based-on-misaligned-pointers
+
+A place is said to be "based on a misaligned pointer" if the last `*` projection
+during place computation was performed on a pointer that was not aligned for its
+type. (If there is no `*` projection in the place expression, then this is
+accessing the field of a local and rustc will guarantee proper alignment. If
+there are multiple `*` projection, then each of them incurs a load of the
+pointer-to-be-dereferenced itself from memory, and each of these loads is
+subject to the alignment constraint. Note that some `*` projections can be
+omitted in surface Rust syntax due to automatic dereferencing; we are
+considering the fully expanded place expression here.)
+
+For instance, if `ptr` has type `*const S` where `S` has an alignment of 8, then
+`ptr` must be 8-aligned or else `(*ptr).f` is "based on an misaligned pointer".
+This is true even if the type of the field `f` is `u8` (i.e., a type with
+alignment 1). In other words, the alignment requirement derives from the type of
+the pointer that was dereferenced, *not* the type of the field that is being
+accessed.
+
+Note that a place based on a misaligned pointer only leads to Undefined Behavior
+when it is loaded from or stored to. `addr_of!`/`addr_of_mut!` on such a place
+is allowed. `&`/`&mut` on a place requires the alignment of the field type (or
+else the program would be "producing an invalid value"), which generally is a
+less restrictive requirement than being based on an aligned pointer. Taking a
+reference will lead to a compiler error in cases where the field type might be
+more aligned than the type that contains it, i.e., `repr(packed)`. This means
+that being based on an aligned pointer is always sufficient to ensure that the
+new reference is aligned, but it is not always necessary.
+
### Dangling pointers
[dangling]: #dangling-pointers
@@ -128,8 +161,11 @@ must never exceed `isize::MAX`.
[Rustonomicon]: ../nomicon/index.html
[`NonNull<T>`]: ../core/ptr/struct.NonNull.html
[`NonZero*`]: ../core/num/index.html
-[dereference expression]: expressions/operator-expr.md#the-dereference-operator
[place expression context]: expressions.md#place-expressions-and-value-expressions
[rules]: inline-assembly.md#rules-for-inline-assembly
[points to]: #pointed-to-bytes
[pointed to]: #pointed-to-bytes
+[offset]: ../std/primitive.pointer.html#method.offset
+[project-field]: expressions/field-expr.md
+[project-tuple]: expressions/tuple-expr.md#tuple-indexing-expressions
+[project-slice]: expressions/array-expr.md#array-and-slice-indexing-expressions
diff --git a/src/doc/reference/src/destructors.md b/src/doc/reference/src/destructors.md
index 17afc3676..9c426426c 100644
--- a/src/doc/reference/src/destructors.md
+++ b/src/doc/reference/src/destructors.md
@@ -156,7 +156,7 @@ temporary variable that holds the result of that expression when used in a
Apart from lifetime extension, the temporary scope of an expression is the
smallest scope that contains the expression and is one of the following:
-* The entire function body.
+* The entire function.
* A statement.
* The body of an [`if`], [`while`] or [`loop`] expression.
* The `else` block of an `if` expression.
@@ -168,8 +168,8 @@ smallest scope that contains the expression and is one of the following:
> **Notes**:
>
> Temporaries that are created in the final expression of a function
-> body are dropped *after* any named variables bound in the function body, as
-> there is no smaller enclosing temporary scope.
+> body are dropped *after* any named variables bound in the function body.
+> Their drop scope is the entire function, as there is no smaller enclosing temporary scope.
>
> The [scrutinee] of a `match` expression is not a temporary scope, so
> temporaries in the scrutinee can be dropped after the `match` expression. For
diff --git a/src/doc/reference/src/expressions/operator-expr.md b/src/doc/reference/src/expressions/operator-expr.md
index 8b6429636..bd4998af5 100644
--- a/src/doc/reference/src/expressions/operator-expr.md
+++ b/src/doc/reference/src/expressions/operator-expr.md
@@ -478,6 +478,16 @@ unsafe {
assert_eq!(values[1], 3);
```
+#### Slice DST pointer to pointer cast
+
+For slice types like `[T]` and `[U]`, the raw pointer types `*const [T]`, `*mut [T]`,
+`*const [U]`, and `*mut [U]` encode the number of elements in this slice. Casts between
+these raw pointer types preserve the number of elements. Note that, as a consequence,
+such casts do *not* necessarily preserve the size of the pointer's referent (e.g.,
+casting `*const [u16]` to `*const [u8]` will result in a raw pointer which refers to an
+object of half the size of the original). The same holds for `str` and any compound type
+whose unsized tail is a slice type, such as struct `Foo(i32, [u8])` or `(u64, Foo)`.
+
## Assignment expressions
> **<sup>Syntax</sup>**\
diff --git a/src/doc/reference/src/inline-assembly.md b/src/doc/reference/src/inline-assembly.md
index 26f1acedc..e905fcb26 100644
--- a/src/doc/reference/src/inline-assembly.md
+++ b/src/doc/reference/src/inline-assembly.md
@@ -414,10 +414,13 @@ Flags are used to further influence the behavior of the inline assembly block.
Currently the following options are defined:
- `pure`: The `asm!` block has no side effects, and its outputs depend only on its direct inputs (i.e. the values themselves, not what they point to) or values read from memory (unless the `nomem` options is also set).
This allows the compiler to execute the `asm!` block fewer times than specified in the program (e.g. by hoisting it out of a loop) or even eliminate it entirely if the outputs are not used.
+ The `pure` option must be combined with either the `nomem` or `readonly` options, otherwise a compile-time error is emitted.
- `nomem`: The `asm!` blocks does not read or write to any memory.
This allows the compiler to cache the values of modified global variables in registers across the `asm!` block since it knows that they are not read or written to by the `asm!`.
+ The compiler also assumes that this `asm!` block does not perform any kind of synchronization with other threads, e.g. via fences.
- `readonly`: The `asm!` block does not write to any memory.
This allows the compiler to cache the values of unmodified global variables in registers across the `asm!` block since it knows that they are not written to by the `asm!`.
+ The compiler also assumes that this `asm!` block does not perform any kind of synchronization with other threads, e.g. via fences.
- `preserves_flags`: The `asm!` block does not modify the flags register (defined in the rules below).
This allows the compiler to avoid recomputing the condition flags after the `asm!` block.
- `noreturn`: The `asm!` block never returns, and its return type is defined as `!` (never).
@@ -432,7 +435,6 @@ Currently the following options are defined:
The compiler performs some additional checks on options:
- The `nomem` and `readonly` options are mutually exclusive: it is a compile-time error to specify both.
-- The `pure` option must be combined with either the `nomem` or `readonly` options, otherwise a compile-time error is emitted.
- It is a compile-time error to specify `pure` on an asm block with no outputs or only discarded outputs (`_`).
- It is a compile-time error to specify `noreturn` on an asm block with outputs.
diff --git a/src/doc/reference/src/items/traits.md b/src/doc/reference/src/items/traits.md
index 3c5d31d5c..828a8a35f 100644
--- a/src/doc/reference/src/items/traits.md
+++ b/src/doc/reference/src/items/traits.md
@@ -43,7 +43,7 @@ trait Example {
}
```
-Trait functions are not allowed to be [`async`] or [`const`].
+Trait functions are not allowed to be [`const`].
## Trait bounds
diff --git a/src/doc/reference/src/types/impl-trait.md b/src/doc/reference/src/types/impl-trait.md
index af900408e..b46c209b7 100644
--- a/src/doc/reference/src/types/impl-trait.md
+++ b/src/doc/reference/src/types/impl-trait.md
@@ -88,6 +88,12 @@ which also avoids the drawbacks of using a boxed trait object.
Similarly, the concrete types of iterators could become very complex, incorporating the types of all previous iterators in a chain.
Returning `impl Iterator` means that a function only exposes the `Iterator` trait as a bound on its return type, instead of explicitly specifying all of the other iterator types involved.
+## Return-position `impl Trait` in traits and trait implementations
+
+Functions in traits may also use `impl Trait` as a syntax for an anonymous associated type.
+
+Every `impl Trait` in the return type of an associated function in a trait is desugared to an anonymous associated type. The return type that appears in the implementation's function signature is used to determine the value of the associated type.
+
### Differences between generics and `impl Trait` in return position
In argument position, `impl Trait` is very similar in semantics to a generic type parameter.
@@ -121,8 +127,8 @@ Instead, the function chooses the return type, but only promises that it will im
## Limitations
-`impl Trait` can only appear as a parameter or return type of a free or inherent function.
-It cannot appear inside implementations of traits, nor can it be the type of a let binding or appear inside a type alias.
+`impl Trait` can only appear as a parameter or return type of a non-`extern` function.
+It cannot be the type of a `let` binding, field type, or appear inside a type alias.
[closures]: closure.md
[_GenericArgs_]: ../paths.md#paths-in-expressions
diff --git a/src/doc/reference/src/types/textual.md b/src/doc/reference/src/types/textual.md
index 41ed35ea1..fcbec0823 100644
--- a/src/doc/reference/src/types/textual.md
+++ b/src/doc/reference/src/types/textual.md
@@ -17,7 +17,9 @@ is valid UTF-8. Calling a `str` method with a non-UTF-8 buffer can cause
Since `str` is a [dynamically sized type], it can only be instantiated through a
pointer type, such as `&str`.
-## Bit validity
+## Layout and bit validity
+
+`char` is guaranteed to have the same size and alignment as `u32` on all platforms.
Every byte of a `char` is guaranteed to be initialized (in other words,
`transmute::<char, [u8; size_of::<char>()]>(...)` is always sound -- but since
diff --git a/src/doc/rust-by-example/src/SUMMARY.md b/src/doc/rust-by-example/src/SUMMARY.md
index b1bcf223d..b8e6ada91 100644
--- a/src/doc/rust-by-example/src/SUMMARY.md
+++ b/src/doc/rust-by-example/src/SUMMARY.md
@@ -161,7 +161,7 @@
- [Unpacking options with `?`](error/option_unwrap/question_mark.md)
- [Combinators: `map`](error/option_unwrap/map.md)
- [Combinators: `and_then`](error/option_unwrap/and_then.md)
- - [Defaults: `or`, `or_else`, `get_or_insert`, 'get_or_insert_with`](error/option_unwrap/defaults.md)
+ - [Defaults: `or`, `or_else`, `get_or_insert`, `get_or_insert_with`](error/option_unwrap/defaults.md)
- [`Result`](error/result.md)
- [`map` for `Result`](error/result/result_map.md)
- [aliases for `Result`](error/result/result_alias.md)
@@ -197,7 +197,7 @@
- [File I/O](std_misc/file.md)
- [`open`](std_misc/file/open.md)
- [`create`](std_misc/file/create.md)
- - [`read lines`](std_misc/file/read_lines.md)
+ - [`read_lines`](std_misc/file/read_lines.md)
- [Child processes](std_misc/process.md)
- [Pipes](std_misc/process/pipe.md)
- [Wait](std_misc/process/wait.md)
diff --git a/src/doc/rust-by-example/src/attribute.md b/src/doc/rust-by-example/src/attribute.md
index dcb7d6c1a..d6a49dc01 100644
--- a/src/doc/rust-by-example/src/attribute.md
+++ b/src/doc/rust-by-example/src/attribute.md
@@ -14,9 +14,34 @@ can be used to/for:
* mark functions that will be part of a benchmark
* [attribute like macros][macros]
-When attributes apply to a whole crate, their syntax is `#![crate_attribute]`,
-and when they apply to a module or item, the syntax is `#[item_attribute]`
-(notice the missing bang `!`).
+Attributes look like `#[outer_attribute]` or `#![inner_attribute]`,
+with the difference between them being where they apply.
+
+- `#[outer_attribute]` applies to the [item][item] immediately
+ following it. Some examples of items are: a function, a module
+ declaration, a constant, a structure, an enum. Here is an example
+ where attribute `#[derive(Debug)]` applies to the struct
+ `Rectangle`:
+ ```rust
+ #[derive(Debug)]
+ struct Rectangle {
+ width: u32,
+ height: u32,
+ }
+ ```
+
+- `#![inner_attribute]` applies to the enclosing [item][item] (typically a
+ module or a crate). In other words, this attribute is intepreted as
+ applying to the entire scope in which it's place. Here is an example
+ where `#![allow(unusude_variables)]` applies to the whole crate (if
+ placed in `main.rs`):
+ ```rust
+ #![allow(unused_variables)]
+
+ fn main() {
+ let x = 3; // This would normally warn about an unused variable.
+ }
+ ```
Attributes can take arguments with different syntaxes:
@@ -36,5 +61,6 @@ Attributes can have multiple values and can be separated over multiple lines, to
[cfg]: attribute/cfg.md
[crate]: attribute/crate.md
+[item]: https://doc.rust-lang.org/stable/reference/items.html
[lint]: https://en.wikipedia.org/wiki/Lint_%28software%29
[macros]: https://doc.rust-lang.org/book/ch19-06-macros.html#attribute-like-macros
diff --git a/src/doc/rust-by-example/src/custom_types/constants.md b/src/doc/rust-by-example/src/custom_types/constants.md
index 8878ba834..c060db734 100644
--- a/src/doc/rust-by-example/src/custom_types/constants.md
+++ b/src/doc/rust-by-example/src/custom_types/constants.md
@@ -4,7 +4,7 @@ Rust has two different types of constants which can be declared in any scope
including global. Both require explicit type annotation:
* `const`: An unchangeable value (the common case).
-* `static`: A possibly `mut`able variable with [`'static`][static] lifetime.
+* `static`: A possibly mutable variable with [`'static`][static] lifetime.
The static lifetime is inferred and does not have to be specified.
Accessing or modifying a mutable static variable is [`unsafe`][unsafe].
diff --git a/src/doc/rust-by-example/src/error/option_unwrap/question_mark.md b/src/doc/rust-by-example/src/error/option_unwrap/question_mark.md
index 831625229..04de93cc7 100644
--- a/src/doc/rust-by-example/src/error/option_unwrap/question_mark.md
+++ b/src/doc/rust-by-example/src/error/option_unwrap/question_mark.md
@@ -8,7 +8,8 @@ function is being executed and return `None`.
```rust,editable
fn next_birthday(current_age: Option<u8>) -> Option<String> {
// If `current_age` is `None`, this returns `None`.
- // If `current_age` is `Some`, the inner `u8` gets assigned to `next_age`
+ // If `current_age` is `Some`, the inner `u8` value + 1
+ // gets assigned to `next_age`
let next_age: u8 = current_age? + 1;
Some(format!("Next year I will be {}", next_age))
}
diff --git a/src/doc/rust-by-example/src/flow_control/while_let.md b/src/doc/rust-by-example/src/flow_control/while_let.md
index 897375a8e..745b7ae75 100644
--- a/src/doc/rust-by-example/src/flow_control/while_let.md
+++ b/src/doc/rust-by-example/src/flow_control/while_let.md
@@ -31,26 +31,24 @@ loop {
Using `while let` makes this sequence much nicer:
```rust,editable
-fn main() {
- // Make `optional` of type `Option<i32>`
- let mut optional = Some(0);
-
- // This reads: "while `let` destructures `optional` into
- // `Some(i)`, evaluate the block (`{}`). Else `break`.
- while let Some(i) = optional {
- if i > 9 {
- println!("Greater than 9, quit!");
- optional = None;
- } else {
- println!("`i` is `{:?}`. Try again.", i);
- optional = Some(i + 1);
- }
- // ^ Less rightward drift and doesn't require
- // explicitly handling the failing case.
+// Make `optional` of type `Option<i32>`
+let mut optional = Some(0);
+
+// This reads: "while `let` destructures `optional` into
+// `Some(i)`, evaluate the block (`{}`). Else `break`.
+while let Some(i) = optional {
+ if i > 9 {
+ println!("Greater than 9, quit!");
+ optional = None;
+ } else {
+ println!("`i` is `{:?}`. Try again.", i);
+ optional = Some(i + 1);
}
- // ^ `if let` had additional optional `else`/`else if`
- // clauses. `while let` does not have these.
+ // ^ Less rightward drift and doesn't require
+ // explicitly handling the failing case.
}
+// ^ `if let` had additional optional `else`/`else if`
+// clauses. `while let` does not have these.
```
### See also:
diff --git a/src/doc/rust-by-example/src/fn/closures.md b/src/doc/rust-by-example/src/fn/closures.md
index 82286003b..e7b8c9867 100644
--- a/src/doc/rust-by-example/src/fn/closures.md
+++ b/src/doc/rust-by-example/src/fn/closures.md
@@ -14,7 +14,7 @@ variable names *must* be specified.
Other characteristics of closures include:
* using `||` instead of `()` around input variables.
-* optional body delimination (`{}`) for a single expression (mandatory otherwise).
+* optional body delimitation (`{}`) for a single expression (mandatory otherwise).
* the ability to capture the outer environment variables.
```rust,editable
@@ -26,7 +26,7 @@ fn main() {
// TODO: uncomment the line above and see the compiler error. The compiler
// suggests that we define a closure instead.
- // Closures are anonymous, here we are binding them to references
+ // Closures are anonymous, here we are binding them to references.
// Annotation is identical to function annotation but is optional
// as are the `{}` wrapping the body. These nameless functions
// are assigned to appropriately named variables.
diff --git a/src/doc/rust-by-example/src/fn/hof.md b/src/doc/rust-by-example/src/fn/hof.md
index 88918cb52..9be5b41fe 100644
--- a/src/doc/rust-by-example/src/fn/hof.md
+++ b/src/doc/rust-by-example/src/fn/hof.md
@@ -10,7 +10,7 @@ fn is_odd(n: u32) -> bool {
}
fn main() {
- println!("Find the sum of all the squared odd numbers under 1000");
+ println!("Find the sum of all the numbers with odd squares under 1000");
let upper = 1000;
// Imperative approach
diff --git a/src/doc/rust-by-example/src/meta/doc.md b/src/doc/rust-by-example/src/meta/doc.md
index e9e51186f..b1732f837 100644
--- a/src/doc/rust-by-example/src/meta/doc.md
+++ b/src/doc/rust-by-example/src/meta/doc.md
@@ -2,7 +2,8 @@
Use `cargo doc` to build documentation in `target/doc`.
-Use `cargo test` to run all tests (including documentation tests), and `cargo test --doc` to only run documentation tests.
+Use `cargo test` to run all tests (including documentation tests), and `cargo
+test --doc` to only run documentation tests.
These commands will appropriately invoke `rustdoc` (and `rustc`) as required.
@@ -67,7 +68,8 @@ $ rustdoc --test --extern doc="libdoc.rlib" doc.rs
## Doc attributes
-Below are a few examples of the most common `#[doc]` attributes used with `rustdoc`.
+Below are a few examples of the most common `#[doc]` attributes used with
+`rustdoc`.
### `inline`
@@ -104,7 +106,8 @@ Using this tells `rustdoc` not to include this in documentation:
pub use self::async_await::*;
```
-For documentation, `rustdoc` is widely used by the community. It's what is used to generate the [std library docs](https://doc.rust-lang.org/std/).
+For documentation, `rustdoc` is widely used by the community. It's what is used
+to generate the [std library docs](https://doc.rust-lang.org/std/).
### See also:
diff --git a/src/doc/rust-by-example/src/meta/playground.md b/src/doc/rust-by-example/src/meta/playground.md
index 7fcfad1a7..e78552d29 100644
--- a/src/doc/rust-by-example/src/meta/playground.md
+++ b/src/doc/rust-by-example/src/meta/playground.md
@@ -1,6 +1,7 @@
# Playground
-The [Rust Playground](https://play.rust-lang.org/) is a way to experiment with Rust code through a web interface.
+The [Rust Playground](https://play.rust-lang.org/) is a way to experiment with
+Rust code through a web interface.
## Using it with `mdbook`
@@ -12,7 +13,9 @@ fn main() {
}
```
-This allows the reader to both run your code sample, but also modify and tweak it. The key here is the adding the word `editable` to your codefence block separated by a comma.
+This allows the reader to both run your code sample, but also modify and tweak
+it. The key here is the adding the word `editable` to your codefence block
+separated by a comma.
````markdown
```rust,editable
@@ -20,7 +23,8 @@ This allows the reader to both run your code sample, but also modify and tweak i
```
````
-Additionally, you can add `ignore` if you want `mdbook` to skip your code when it builds and tests.
+Additionally, you can add `ignore` if you want `mdbook` to skip your code when
+it builds and tests.
````markdown
```rust,editable,ignore
@@ -30,7 +34,10 @@ Additionally, you can add `ignore` if you want `mdbook` to skip your code when i
## Using it with docs
-You may have noticed in some of the [official Rust docs][official-rust-docs] a button that says "Run", which opens the code sample up in a new tab in Rust Playground. This feature is enabled if you use the #[doc] attribute called [`html_playground_url`][html-playground-url].
+You may have noticed in some of the [official Rust docs][official-rust-docs] a
+button that says "Run", which opens the code sample up in a new tab in Rust
+Playground. This feature is enabled if you use the `#[doc]` attribute called
+[`html_playground_url`][html-playground-url].
### See also:
diff --git a/src/doc/rust-by-example/src/primitives/array.md b/src/doc/rust-by-example/src/primitives/array.md
index 5f5e69944..704a2131c 100644
--- a/src/doc/rust-by-example/src/primitives/array.md
+++ b/src/doc/rust-by-example/src/primitives/array.md
@@ -6,7 +6,7 @@ at compile time, is part of their type signature `[T; length]`.
Slices are similar to arrays, but their length is not known at compile time.
Instead, a slice is a two-word object; the first word is a pointer to the data,
-the second word the length of the slice. The word size is the same as usize,
+the second word is the length of the slice. The word size is the same as usize,
determined by the processor architecture, e.g. 64 bits on an x86-64. Slices can
be used to borrow a section of an array and have the type signature `&[T]`.
diff --git a/src/doc/rust-by-example/src/scope/lifetime.md b/src/doc/rust-by-example/src/scope/lifetime.md
index 68b42d380..01c4bf405 100644
--- a/src/doc/rust-by-example/src/scope/lifetime.md
+++ b/src/doc/rust-by-example/src/scope/lifetime.md
@@ -1,6 +1,6 @@
# Lifetimes
-A *lifetime* is a construct of the compiler (or more specifically, its *borrow
+A *lifetime* is a construct the compiler (or more specifically, its *borrow
checker*) uses to ensure all borrows are valid. Specifically, a variable's
lifetime begins when it is created and ends when it is destroyed. While
lifetimes and scopes are often referred to together, they are not the same.
diff --git a/src/doc/rust-by-example/src/scope/move.md b/src/doc/rust-by-example/src/scope/move.md
index 0433e8ac1..df631ee08 100644
--- a/src/doc/rust-by-example/src/scope/move.md
+++ b/src/doc/rust-by-example/src/scope/move.md
@@ -1,7 +1,7 @@
# Ownership and moves
Because variables are in charge of freeing their own resources,
-**resources can only have one owner**. This also prevents resources
+**resources can only have one owner**. This prevents resources
from being freed more than once. Note that not all variables own
resources (e.g. [references]).
diff --git a/src/doc/rust-by-example/src/std/hash/alt_key_types.md b/src/doc/rust-by-example/src/std/hash/alt_key_types.md
index ab94819b2..7e68e8c67 100644
--- a/src/doc/rust-by-example/src/std/hash/alt_key_types.md
+++ b/src/doc/rust-by-example/src/std/hash/alt_key_types.md
@@ -3,7 +3,7 @@
Any type that implements the `Eq` and `Hash` traits can be a key in `HashMap`.
This includes:
-* `bool` (though not very useful since there is only two possible keys)
+* `bool` (though not very useful since there are only two possible keys)
* `int`, `uint`, and all variations thereof
* `String` and `&str` (protip: you can have a `HashMap` keyed by `String`
and call `.get()` with an `&str`)
diff --git a/src/doc/rust-by-example/src/std_misc/arg/matching.md b/src/doc/rust-by-example/src/std_misc/arg/matching.md
index 4cb68eabf..4a96fbc21 100644
--- a/src/doc/rust-by-example/src/std_misc/arg/matching.md
+++ b/src/doc/rust-by-example/src/std_misc/arg/matching.md
@@ -70,6 +70,9 @@ fn main() {
}
```
+If you named your program `match_args.rs` and compile it like this `rustc
+match_args.rs`, you can execute it as follows:
+
```shell
$ ./match_args Rust
This is not the answer.
diff --git a/src/doc/rust-by-example/src/std_misc/file/read_lines.md b/src/doc/rust-by-example/src/std_misc/file/read_lines.md
index 216b0181c..1a2a64241 100644
--- a/src/doc/rust-by-example/src/std_misc/file/read_lines.md
+++ b/src/doc/rust-by-example/src/std_misc/file/read_lines.md
@@ -56,15 +56,13 @@ fn main() {
// File hosts.txt must exist in the current path
if let Ok(lines) = read_lines("./hosts.txt") {
// Consumes the iterator, returns an (Optional) String
- for line in lines {
- if let Ok(ip) = line {
- println!("{}", ip);
- }
+ for line in lines.flatten() {
+ println!("{}", line);
}
}
}
-// The output is wrapped in a Result to allow matching on errors
+// The output is wrapped in a Result to allow matching on errors.
// Returns an Iterator to the Reader of the lines of the file.
fn read_lines<P>(filename: P) -> io::Result<io::Lines<io::BufReader<File>>>
where P: AsRef<Path>, {
diff --git a/src/doc/rust-by-example/src/std_misc/threads/testcase_mapreduce.md b/src/doc/rust-by-example/src/std_misc/threads/testcase_mapreduce.md
index ee25b1661..9cc162f51 100644
--- a/src/doc/rust-by-example/src/std_misc/threads/testcase_mapreduce.md
+++ b/src/doc/rust-by-example/src/std_misc/threads/testcase_mapreduce.md
@@ -30,7 +30,7 @@ use std::thread;
fn main() {
// This is our data to process.
- // We will calculate the sum of all digits via a threaded map-reduce algorithm.
+ // We will calculate the sum of all digits via a threaded map-reduce algorithm.
// Each whitespace separated chunk will be handled in a different thread.
//
// TODO: see what happens to the output if you insert spaces!
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 70e77fd5b..fc0a5f469 100644
--- a/src/doc/rustc-dev-guide/examples/rustc-driver-example.rs
+++ b/src/doc/rustc-dev-guide/examples/rustc-driver-example.rs
@@ -63,6 +63,8 @@ fn main() {
// Registry of diagnostics codes.
registry: registry::Registry::new(&rustc_error_codes::DIAGNOSTICS),
make_codegen_backend: None,
+ expanded_args: Vec::new(),
+ ice_file: None,
};
rustc_interface::run_compiler(config, |compiler| {
compiler.enter(|queries| {
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 888674aaf..0195224de 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
@@ -73,6 +73,8 @@ fn main() {
override_queries: None,
registry: registry::Registry::new(&rustc_error_codes::DIAGNOSTICS),
make_codegen_backend: None,
+ expanded_args: Vec::new(),
+ ice_file: None,
};
rustc_interface::run_compiler(config, |compiler| {
compiler.enter(|queries| {
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 df0e0385d..3d3827e5d 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
@@ -51,6 +51,8 @@ fn main() {
override_queries: None,
make_codegen_backend: None,
registry: registry::Registry::new(&rustc_error_codes::DIAGNOSTICS),
+ expanded_args: Vec::new(),
+ ice_file: None,
};
rustc_interface::run_compiler(config, |compiler| {
compiler.enter(|queries| {
diff --git a/src/doc/rustc-dev-guide/src/SUMMARY.md b/src/doc/rustc-dev-guide/src/SUMMARY.md
index 41e16c5e6..4a7fd2b77 100644
--- a/src/doc/rustc-dev-guide/src/SUMMARY.md
+++ b/src/doc/rustc-dev-guide/src/SUMMARY.md
@@ -124,6 +124,7 @@
- [Goals and clauses](./traits/goals-and-clauses.md)
- [Canonical queries](./traits/canonical-queries.md)
- [Next-gen trait solving](./solve/trait-solving.md)
+ - [Invariants of the type system](./solve/invariants.md)
- [The solver](./solve/the-solver.md)
- [Canonicalization](./solve/canonicalization.md)
- [Coinduction](./solve/coinduction.md)
@@ -135,6 +136,7 @@
- [Opaque Types](./opaque-types-type-alias-impl-trait.md)
- [Inference details](./opaque-types-impl-trait-inference.md)
- [Return Position Impl Trait In Trait](./return-position-impl-trait-in-trait.md)
+- [Effect checking](./effects.md)
- [Pattern and Exhaustiveness Checking](./pat-exhaustive-checking.md)
- [MIR dataflow](./mir/dataflow.md)
- [Drop elaboration](./mir/drop-elaboration.md)
diff --git a/src/doc/rustc-dev-guide/src/appendix/bibliography.md b/src/doc/rustc-dev-guide/src/appendix/bibliography.md
index c2bb00e3b..cc262abef 100644
--- a/src/doc/rustc-dev-guide/src/appendix/bibliography.md
+++ b/src/doc/rustc-dev-guide/src/appendix/bibliography.md
@@ -79,7 +79,7 @@ Rust, as well as publications about Rust.
Rust](https://munksgaard.me/papers/laumann-munksgaard-larsen.pdf). Philip
Munksgaard's master's thesis. Research for Servo.
* [Ownership is Theft: Experiences Building an Embedded OS in Rust - Amit Levy, et. al.](https://amitlevy.com/papers/tock-plos2015.pdf)
-* [You can't spell trust without Rust](https://raw.githubusercontent.com/Gankro/thesis/master/thesis.pdf). Alexis Beingessner's master's thesis.
+* [You can't spell trust without Rust](https://faultlore.com/blah/papers/thesis.pdf). Aria Beingessner's master's thesis.
* [Rust-Bio: a fast and safe bioinformatics library](https://academic.oup.com/bioinformatics/article/32/3/444/1743419). Johannes Köster
* [Safe, Correct, and Fast Low-Level Networking](https://octarineparrot.com/assets/msci_paper.pdf). Robert Clipsham's master's thesis.
* [Formalizing Rust traits](https://open.library.ubc.ca/cIRcle/collections/ubctheses/24/items/1.0220521). Jonatan Milewski's master's thesis.
diff --git a/src/doc/rustc-dev-guide/src/appendix/glossary.md b/src/doc/rustc-dev-guide/src/appendix/glossary.md
index 27b6cddf2..ee3a3a720 100644
--- a/src/doc/rustc-dev-guide/src/appendix/glossary.md
+++ b/src/doc/rustc-dev-guide/src/appendix/glossary.md
@@ -25,6 +25,7 @@ Term | Meaning
<span id="drop-glue">drop glue</span> &nbsp; | (internal) compiler-generated instructions that handle calling the destructors (`Drop`) for data types.
<span id="dst">DST</span> &nbsp; | 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]`).
<span id="ebl">early-bound lifetime</span> &nbsp; | A lifetime region that is substituted at its definition site. Bound in an item's `Generics` and substituted using a `GenericArgs`. Contrast with **late-bound lifetime**. ([see more](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_type_ir/sty/enum.RegionKind.html#bound-regions))
+<span id="effect">effects</span> &nbsp; | Right now only means const traits and `~const` bounds. ([see more](../effects.md))
<span id="empty-type">empty type</span> &nbsp; | see "uninhabited type".
<span id="fat-ptr">fat pointer</span> &nbsp; | 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".
<span id="free-var">free variable</span> &nbsp; | 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)
@@ -93,6 +94,7 @@ Term | Meaning
<span id="upvar">upvar</span> &nbsp; | A variable captured by a closure from outside the closure.
<span id="variance">variance</span> &nbsp; | Determines how changes to a generic type/lifetime parameter affect subtyping; for example, if `T` is a subtype of `U`, then `Vec<T>` is a subtype `Vec<U>` because `Vec` is *covariant* in its generic parameter. See [the background chapter](./background.md#variance) for a more general explanation. See the [variance chapter](../variance.md) for an explanation of how type checking handles variance.
<span id="variant-idx">variant index</span> &nbsp; | In an enum, identifies a variant by assigning them indices starting at 0. This is purely internal and not to be confused with the ["discriminant"](#discriminant) which can be overwritten by the user (e.g. `enum Bool { True = 42, False = 0 }`).
+<span id="WF">Well-formedness</span> &nbsp; | Semantically:An expression that evaluates to meaningful result. In Type Systems: A type related construct which follows rules of the type system.
<span id="wide-ptr">wide pointer</span> &nbsp; | A pointer with additional metadata. See "fat pointer" for more.
<span id="zst">ZST</span> &nbsp; | Zero-Sized Type. A type whose values have size 0 bytes. Since `2^0 = 1`, such types can have exactly one value. For example, `()` (unit) is a ZST. `struct Foo;` is also a ZST. The compiler can do some nice optimizations around ZSTs.
diff --git a/src/doc/rustc-dev-guide/src/building/suggested.md b/src/doc/rustc-dev-guide/src/building/suggested.md
index cb722696e..1336ff629 100644
--- a/src/doc/rustc-dev-guide/src/building/suggested.md
+++ b/src/doc/rustc-dev-guide/src/building/suggested.md
@@ -277,7 +277,6 @@ let
# `config.toml.example`) from `1bd30ce2aac40c7698aa4a1b9520aa649ff2d1c5`
config = pkgs.writeText "rustc-config" ''
profile = "compiler" # you may want to choose a different profile, like `library` or `tools`
- changelog-seen = 2
[build]
patch-binaries-for-nix = true
diff --git a/src/doc/rustc-dev-guide/src/effects.md b/src/doc/rustc-dev-guide/src/effects.md
new file mode 100644
index 000000000..1fda7bcbb
--- /dev/null
+++ b/src/doc/rustc-dev-guide/src/effects.md
@@ -0,0 +1,66 @@
+# Effects and effect checking
+
+Note: all of this describes the implementation of the unstable `effects` and
+`const_trait_impl` features. None of this implementation is usable or visible from
+stable Rust.
+
+The implementation of const traits and `~const` bounds is a limited effect system.
+It is used to allow trait bounds on `const fn` to be used within the `const fn` for
+method calls. Within the function, in order to know whether a method on a trait
+bound is `const`, we need to know whether there is a `~const` bound for the trait.
+In order to know whether we can instantiate a `~const` bound on a `const fn`, we
+need to know whether there is a `const_trait` impl for the type and trait being
+used (or whether the `const fn` is used at runtime, then any type implementing the
+trait is ok, just like with other bounds).
+
+We perform these checks via a const generic boolean that gets attached to all
+`const fn` and `const trait`. The following sections will explain the desugarings
+and the way we perform the checks at call sites.
+
+The const generic boolean is inverted to the meaning of `const`. In the compiler
+it is called `host`, because it enables "host APIs" like `static` items, network
+access, disk access, random numbers and everything else that isn't available in
+`const` contexts. So `false` means "const", `true` means "not const" and if it's
+a generic parameter, it means "maybe const" (meaning we're in a const fn or const
+trait).
+
+## `const fn`
+
+All `const fn` have a `#[rustc_host] const host: bool` generic parameter that is
+hidden from users. Any `~const Trait` bounds in the generics list or `where` bounds
+of a `const fn` get converted to `Trait<host> + Trait<true>` bounds. The `Trait<true>`
+exists so that associated types of the generic param can be used from projections
+like `<T as Trait>::Assoc`, because there are no `<T as ~const Trait>` projections for now.
+
+## `#[const_trait] trait`s
+
+The `#[const_trait]` attribute gives the marked trait a `#[rustc_host] const host: bool`
+generic parameter. All functions of the trait "inherit" this generic parameter, just like
+they have all the regular generic parameters of the trait. Any `~const Trait` super-trait
+bounds get desugared to `Trait<host> + Trait<true>` in order to allow using associated
+types and consts of the super traits in the trait declaration. This is necessary, because
+`<Self as SuperTrait>::Assoc` is always `<Self as SuperTrait<true>>::Assoc` as there is
+no `<Self as ~const SuperTrait>` syntax.
+
+## `typeck` performing method and function call checks.
+
+When generic parameters are instantiated for any items, the `host` generic parameter
+is always instantiated as an inference variable. This is a special kind of inference var
+that is not part of the type or const inference variables, similar to how we have
+special inference variables for type variables that we know to be an integer, but not
+yet which one. These separate inference variables fall back to `true` at
+the end of typeck (in `fallback_effects`) to ensure that `let _ = some_fn_item_name;`
+will keep compiling.
+
+All actually used (in function calls, casts, or anywhere else) function items, will
+have the `enforce_context_effects` method invoked.
+It trivially returns if the function being called has no `host` generic parameter.
+
+In order to error if a non-const function is called in a const context, we have not
+yet disabled the const-check logic that happens on MIR, because
+`enforce_context_effects` does not yet perform this check.
+
+The function call's `host` parameter is then equated to the context's `host` value,
+which almost always trivially succeeds, as it was an inference var. If the inference
+var has already been bound (since the function item is invoked twice), the second
+invocation checks it against the first.
diff --git a/src/doc/rustc-dev-guide/src/feature-gates.md b/src/doc/rustc-dev-guide/src/feature-gates.md
index 8ad4fea1f..788f93d66 100644
--- a/src/doc/rustc-dev-guide/src/feature-gates.md
+++ b/src/doc/rustc-dev-guide/src/feature-gates.md
@@ -20,12 +20,12 @@ See ["Stability in code"][adding] in the "Implementing new features" section for
To remove a feature gate, follow these steps:
-1. Remove the feature gate declaration in `rustc_feature/src/active.rs`.
+1. Remove the feature gate declaration in `rustc_feature/src/unstable.rs`.
It will look like this:
```rust,ignore
/// description of feature
- (active, $feature_name, "$version", Some($tracking_issue_number), $edition)
+ (unstable, $feature_name, "$version", Some($tracking_issue_number), $edition)
```
2. Add a modified version of the feature gate declaration that you just
@@ -45,12 +45,12 @@ To remove a feature gate, follow these steps:
To rename a feature gate, follow these steps (the first two are the same steps
to follow when [removing a feature gate][removing]):
-1. Remove the old feature gate declaration in `rustc_feature/src/active.rs`.
+1. Remove the old feature gate declaration in `rustc_feature/src/unstable.rs`.
It will look like this:
```rust,ignore
/// description of feature
- (active, $old_feature_name, "$version", Some($tracking_issue_number), $edition)
+ (unstable, $old_feature_name, "$version", Some($tracking_issue_number), $edition)
```
2. Add a modified version of the old feature gate declaration that you just
@@ -64,12 +64,12 @@ to follow when [removing a feature gate][removing]):
```
3. Add a feature gate declaration with the new name to
- `rustc_feature/src/active.rs`. It should look very similar to the old
+ `rustc_feature/src/unstable.rs`. It should look very similar to the old
declaration:
```rust,ignore
/// description of feature
- (active, $new_feature_name, "$version", Some($tracking_issue_number), $edition)
+ (unstable, $new_feature_name, "$version", Some($tracking_issue_number), $edition)
```
diff --git a/src/doc/rustc-dev-guide/src/hir-debugging.md b/src/doc/rustc-dev-guide/src/hir-debugging.md
index c25a558a0..5a0bda208 100644
--- a/src/doc/rustc-dev-guide/src/hir-debugging.md
+++ b/src/doc/rustc-dev-guide/src/hir-debugging.md
@@ -1,6 +1,13 @@
# HIR Debugging
-The `-Z unpretty=hir-tree` flag will dump out the HIR.
+Use the `-Z unpretty=hir` flag to produce a human-readable representation of the HIR.
+For cargo projects this can be done with `cargo rustc -- -Z unpretty=hir`.
+This output is useful when you need to see at a glance how your code was desugared and transformed
+during AST lowering.
+
+For a full `Debug` dump of the data in the HIR, use the `-Z unpretty=hir-tree` flag.
+This may be useful when you need to see the full structure of the HIR from the perspective of the
+compiler.
If you are trying to correlate `NodeId`s or `DefId`s with source code, the
`-Z unpretty=expanded,identified` flag may be useful.
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 01508889f..427589dab 100644
--- a/src/doc/rustc-dev-guide/src/implementing_new_features.md
+++ b/src/doc/rustc-dev-guide/src/implementing_new_features.md
@@ -123,12 +123,12 @@ a new unstable feature:
1. Add the feature name to `rustc_span/src/symbol.rs` in the `Symbols {...}` block.
-1. Add a feature gate declaration to `rustc_feature/src/active.rs` in the active
+1. Add a feature gate declaration to `rustc_feature/src/unstable.rs` in the unstable
`declare_features` block.
```rust ignore
/// description of feature
- (active, $feature_name, "CURRENT_RUSTC_VERSION", Some($tracking_issue_number), $edition)
+ (unstable, $feature_name, "CURRENT_RUSTC_VERSION", Some($tracking_issue_number), $edition)
```
where `$edition` has the type `Option<Edition>`, and is typically just `None`. If you haven't yet
@@ -140,7 +140,7 @@ a new unstable feature:
```rust ignore
/// Allows defining identifiers beyond ASCII.
- (active, non_ascii_idents, "CURRENT_RUSTC_VERSION", Some(55467), None),
+ (unstable, non_ascii_idents, "CURRENT_RUSTC_VERSION", Some(55467), None),
```
Features can be marked as incomplete, and trigger the warn-by-default [`incomplete_features`
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 9379a57f6..fd215e3e9 100644
--- a/src/doc/rustc-dev-guide/src/llvm-coverage-instrumentation.md
+++ b/src/doc/rustc-dev-guide/src/llvm-coverage-instrumentation.md
@@ -73,21 +73,21 @@ When compiling with `-C instrument-coverage`,
Coverage instrumentation is performed on the MIR with a [MIR pass][mir-passes]
called [`InstrumentCoverage`][mir-instrument-coverage]. This MIR pass analyzes
the control flow graph (CFG)--represented by MIR `BasicBlock`s--to identify
-code branches, and injects additional [`Coverage`][coverage-statement]
-statements into the `BasicBlock`s.
+code branches, attaches [`FunctionCoverageInfo`] to the function's body,
+and injects additional [`Coverage`][coverage-statement] statements into the
+`BasicBlock`s.
A MIR `Coverage` statement is a virtual instruction that indicates a counter
should be incremented when its adjacent statements are executed, to count
a span of code ([`CodeRegion`][code-region]). It counts the number of times a
-branch is executed, and also specifies the exact location of that code span in
-the Rust source code.
+branch is executed, and is referred to by coverage mappings in the function's
+coverage-info struct.
-Note that many of these `Coverage` statements will _not_ be converted into
+Note that many coverage counters will _not_ be converted into
physical counters (or any other executable instructions) in the final binary.
-Some of them will be (see [`CoverageKind::Counter`]),
+Some of them will be (see [`CoverageKind::CounterIncrement`]),
but other counters can be computed on the fly, when generating a coverage
-report, by mapping a `CodeRegion` to a
-[`CoverageKind::Expression`].
+report, by mapping a `CodeRegion` to a coverage-counter _expression_.
As an example:
@@ -121,8 +121,8 @@ determines when to break out of a loop (a `while` condition, or an `if` or
`match` with a `break`). In MIR, this is typically lowered to a `SwitchInt`,
with one branch to stay in the loop, and another branch to break out of the
loop. The branch that breaks out will almost always execute less often,
-so `InstrumentCoverage` chooses to add a `Counter` to that branch, and an
-`Expression(continue) = Counter(loop) - Counter(break)` to the branch that
+so `InstrumentCoverage` chooses to add a `CounterIncrement` to that branch, and
+uses an expression (`Counter(loop) - Counter(break)`) for the branch that
continues.
The `InstrumentCoverage` MIR pass is documented in
@@ -130,9 +130,9 @@ The `InstrumentCoverage` MIR pass is documented in
[mir-passes]: mir/passes.md
[mir-instrument-coverage]: https://github.com/rust-lang/rust/tree/master/compiler/rustc_mir_transform/src/coverage
+[`FunctionCoverageInfo`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/mir/coverage/struct.FunctionCoverageInfo.html
[code-region]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/mir/coverage/struct.CodeRegion.html
-[`CoverageKind::Counter`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/mir/coverage/enum.CoverageKind.html#variant.Counter
-[`CoverageKind::Expression`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/mir/coverage/enum.CoverageKind.html#variant.Expression
+[`CoverageKind::CounterIncrement`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/mir/coverage/enum.CoverageKind.html#variant.CounterIncrement
[coverage-statement]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/mir/enum.StatementKind.html#variant.Coverage
[instrument-coverage-pass-details]: #implementation-details-of-the-instrumentcoverage-mir-pass
@@ -150,40 +150,38 @@ MIR `Statement` into some backend-specific action or instruction.
match statement.kind {
...
mir::StatementKind::Coverage(box ref coverage) => {
- self.codegen_coverage(&mut bx, coverage.clone(), statement.source_info.scope);
- bx
+ self.codegen_coverage(bx, coverage, statement.source_info.scope);
}
```
-`codegen_coverage()` handles each `CoverageKind` as follows:
+`codegen_coverage()` handles inlined statements and then forwards the coverage
+statement to [`Builder::add_coverage`], which handles each `CoverageKind` as
+follows:
-- For all `CoverageKind`s, Coverage data (counter ID, expression equation
- and ID, and code regions) are passed to the backend's `Builder`, to
- populate data structures that will be used to generate the crate's
- "Coverage Map". (See the [`FunctionCoverage`][function-coverage] `struct`.)
-- For `CoverageKind::Counter`s, an instruction is injected in the backend
+
+- For both `CounterIncrement` and `ExpressionUsed`, the underlying counter or
+ expression ID is passed through to the corresponding [`FunctionCoverage`]
+ struct to indicate that the corresponding regions of code were not removed
+ by MIR optimizations.
+- For `CoverageKind::CounterIncrement`s, an instruction is injected in the backend
IR to increment the physical counter, by calling the `BuilderMethod`
[`instrprof_increment()`][instrprof-increment].
```rust
- pub fn codegen_coverage(&self, bx: &mut Bx, coverage: Coverage, scope: SourceScope) {
+ fn add_coverage(&mut self, instance: Instance<'tcx>, coverage: &Coverage) {
...
- let instance = ... // the scoped instance (current or inlined function)
- let Coverage { kind, code_region } = coverage;
- match kind {
- CoverageKind::Counter { function_source_hash, id } => {
- ...
- bx.add_coverage_counter(instance, id, code_region);
+ let Coverage { kind } = coverage;
+ match *kind {
+ CoverageKind::CounterIncrement { id } => {
+ func_coverage.mark_counter_id_seen(id);
...
bx.instrprof_increment(fn_name, hash, num_counters, index);
}
- CoverageKind::Expression { id, lhs, op, rhs } => {
- bx.add_coverage_counter_expression(instance, id, lhs, op, rhs, code_region);
+ CoverageKind::ExpressionUsed { id } => {
+ func_coverage.mark_expression_id_seen(id);
}
- CoverageKind::Unreachable => {
- bx.add_coverage_unreachable(
- instance,
- code_region.expect(...
+ }
+ }
```
> The function name `instrprof_increment()` is taken from the LLVM intrinsic
@@ -199,7 +197,8 @@ statements is only implemented for LLVM, at this time.
[backend-lowering-mir]: backend/lowering-mir.md
[codegen-statement]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_codegen_ssa/mir/struct.FunctionCx.html#method.codegen_statement
[codegen-coverage]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_codegen_ssa/mir/struct.FunctionCx.html#method.codegen_coverage
-[function-coverage]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_codegen_llvm/coverageinfo/map_data/struct.FunctionCoverage.html
+[`Builder::add_coverage`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_codegen_llvm/builder/struct.Builder.html#method.add_coverage
+[`FunctionCoverage`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_codegen_llvm/coverageinfo/map_data/struct.FunctionCoverage.html
[instrprof-increment]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_codegen_ssa/traits/trait.BuilderMethods.html#tymethod.instrprof_increment
### Coverage Map Generation
@@ -327,9 +326,10 @@ Instrumentor::new(&self.name(), tcx, mir_body).inject_counters();
The `CoverageGraph` is a coverage-specific simplification of the MIR control
flow graph (CFG). Its nodes are [`BasicCoverageBlock`s][bcb], which
encompass one or more sequentially-executed MIR `BasicBlock`s
-(with no internal branching), plus a `CoverageKind` counter (to
-be added, via coverage analysis), and an optional set of additional counters
-to count incoming edges (if there are more than one).
+(with no internal branching).
+
+Nodes and edges in the graph can have associated [`BcbCounter`]s, which are
+stored in [`CoverageCounters`].
The `Instrumentor`'s `inject_counters()` uses the `CoverageGraph` to
compute the best places to inject coverage counters, as MIR `Statement`s,
@@ -338,16 +338,15 @@ with the following steps:
1. [`generate_coverage_spans()`][generate-coverage-spans] computes the minimum set of distinct,
non-branching code regions, from the MIR. These `CoverageSpan`s
represent a span of code that must be counted.
-2. [`make_bcb_counters()`][make-bcb-counters] generates `CoverageKind::Counter`s and
- `CoverageKind::Expression`s for each `CoverageSpan`, plus additional
- `intermediate_expressions`[^intermediate-expressions], not associated with any `CodeRegion`, but
+2. [`make_bcb_counters()`][make-bcb-counters] generates `BcbCounter::Counter`s and
+ `BcbCounter::Expression`s for each `CoverageSpan`, plus additional
+ _intermediate expressions_[^intermediate-expressions] that are not associated
+ with any `CodeRegion`, but
are required to compute a final `Expression` value for a `CodeRegion`.
3. Inject the new counters into the MIR, as new `StatementKind::Coverage`
- statements. This is done by three distinct functions:
- - `inject_coverage_span_counters()`
- - `inject_indirect_counters()`
- - `inject_intermediate_expression()`, called for each intermediate expression
- returned from `make_bcb_counters()`
+ statements.
+4. Attach all other necessary coverage information to the function's body as
+ [`FunctionCoverageInfo`].
[^intermediate-expressions]: Intermediate expressions are sometimes required
because `Expression`s are limited to binary additions or subtractions. For
@@ -359,7 +358,8 @@ intermediate expression for `B - C`.
[coverage-graph]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir_transform/coverage/graph/struct.CoverageGraph.html
[inject-counters]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir_transform/coverage/struct.Instrumentor.html#method.inject_counters
[bcb]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir_transform/coverage/graph/struct.BasicCoverageBlock.html
-[debug]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir_transform/coverage/debug
+[`BcbCounter`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir_transform/coverage/counters/enum.BcbCounter.html
+[`CoverageCounters`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir_transform/coverage/counters/struct.CoverageCounters.html
[generate-coverage-spans]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir_transform/coverage/spans/struct.CoverageSpans.html#method.generate_coverage_spans
[make-bcb-counters]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir_transform/coverage/counters/struct.BcbCounters.html#method.make_bcb_counters
@@ -505,34 +505,3 @@ its `Counter` or `Expression`.
[bcb-counters]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir_transform/coverage/counters/struct.BcbCounters.html
[traverse-coverage-graph-with-loops]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir_transform/coverage/graph/struct.TraverseCoverageGraphWithLoops.html
-
-### Injecting counters into a MIR `BasicBlock`
-
-With the refined `CoverageSpan`s, and after all `Counter`s and `Expression`s are
-created, the final step is to inject the `StatementKind::Coverage` statements
-into the MIR. There are three distinct sources, handled by the following
-functions:
-
-- [`inject_coverage_span_counters()`][inject-coverage-span-counters] injects the
- counter from each `CoverageSpan`'s BCB.
-- [`inject_indirect_counters()`][inject-indirect-counters] injects counters
- for any BCB not assigned to a `CoverageSpan`, and for all edge counters.
- These counters don't have `CoverageSpan`s.
-- [`inject_intermediate_expression()`][inject-intermediate-expression] injects
- the intermediate expressions returned from `make_bcb_counters()`. These
- counters aren't associated with any BCB, edge, or `CoverageSpan`.
-
-These three functions inject the `Coverage` statements into the MIR.
-`Counter`s and `Expression`s with `CoverageSpan`s add `Coverage` statements
-to a corresponding `BasicBlock`, with a `CodeRegion` computed from the
-refined `Span` and current `SourceMap`.
-
-All other `Coverage` statements have a `CodeRegion` of `None`, but they
-still must be injected because they contribute to other `Expression`s.
-
-Finally, edge's with a `CoverageKind::Counter` require a new `BasicBlock`,
-so the counter is only incremented when traversing the branch edge.
-
-[inject-coverage-span-counters]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir_transform/coverage/struct.Instrumentor.html#method.inject_coverage_span_counters
-[inject-indirect-counters]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir_transform/coverage/struct.Instrumentor.html#method.inject_indirect_counters
-[inject-intermediate-expression]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir_transform/coverage/fn.inject_intermediate_expression.html
diff --git a/src/doc/rustc-dev-guide/src/return-position-impl-trait-in-trait.md b/src/doc/rustc-dev-guide/src/return-position-impl-trait-in-trait.md
index 03c7fb6b7..9ed62c87c 100644
--- a/src/doc/rustc-dev-guide/src/return-position-impl-trait-in-trait.md
+++ b/src/doc/rustc-dev-guide/src/return-position-impl-trait-in-trait.md
@@ -298,6 +298,24 @@ types in the impl, since this mapping describes the type that should
come after the `=` in `type Assoc = ...` for each RPITIT.
</details>
+##### Implied bounds in RPITIT hidden type inference
+
+Since `collect_return_position_impl_trait_in_trait_tys` does fulfillment and
+region resolution, we must provide it `assumed_wf_types` so that we can prove
+region obligations with the same expected implied bounds as
+`compare_method_predicate_entailment` does.
+
+Since the return type of a method is understood to be one of the assumed WF
+types, and we eagerly fold the return type with inference variables to do
+opaque type inference, after opaque type inference, the return type will
+resolve to contain the hidden types of the RPITITs. this would mean that the
+hidden types of the RPITITs would be assumed to be well-formed without having
+independently proven that they are. This resulted in a
+[subtle unsoundness bug](https://github.com/rust-lang/rust/pull/116072). In
+order to prevent this cyclic reasoning, we instead replace the hidden types of
+the RPITITs in the return type of the method with *placeholders*, which lead
+to no implied well-formedness bounds.
+
#### Default trait body
Type-checking a default trait body, like:
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 47b9fb5d9..95e3c7cc8 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,7 +7,7 @@
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 <!-- date-check: mar 2023 --> `nightly-2023-03-27`:
+with <!-- date-check: oct 2023 --> `nightly-2023-10-03`:
```rust
{{#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 4edbbca00..fc119c1ec 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,7 +5,7 @@
## 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 <!-- date-check: mar 2023 --> `nightly-2023-03-27`:
+The following was tested with <!-- date-check: oct 2023 --> `nightly-2023-10-03`:
```rust
{{#include ../examples/rustc-driver-interacting-with-the-ast.rs}}
diff --git a/src/doc/rustc-dev-guide/src/solve/invariants.md b/src/doc/rustc-dev-guide/src/solve/invariants.md
new file mode 100644
index 000000000..75ae53070
--- /dev/null
+++ b/src/doc/rustc-dev-guide/src/solve/invariants.md
@@ -0,0 +1,154 @@
+# Invariants of the type system
+
+FIXME: This file talks about invariants of the type system as a whole, not only the solver
+
+There are a lot of invariants - things the type system guarantees to be true at all times -
+which are desirable or expected from other languages and type systems. Unfortunately, quite
+a few of them do not hold in Rust right now. This is either a fundamental to its design or
+caused by bugs and something that may change in the future.
+
+It is important to know about the things you can assume while working on - and with - the
+type system, so here's an incomplete and inofficial list of invariants of
+the core type system:
+
+- ✅: this invariant mostly holds, with some weird exceptions, you can rely on it outside
+of these cases
+- ❌: this invariant does not hold, either due to bugs or by design, you must not rely on
+it for soundness or have to be incredibly careful when doing so
+
+### `wf(X)` implies `wf(normalize(X))` ✅
+
+If a type containing aliases is well-formed, it should also be
+well-formed after normalizing said aliases. We rely on this as
+otherwise we would have to re-check for well-formedness for these
+types.
+
+This is unfortunately broken for `<fndef as FnOnce<..>>::Output` due to implied bounds,
+resulting in [#114936].
+
+### Structural equality modulo regions implies semantic equality ✅
+
+If you have a some type and equate it to itself after replacing any regions with unique
+inference variables in both the lhs and rhs, the now potentially structurally different
+types should still be equal to each other.
+
+Needed to prevent goals from succeeding in HIR typeck and then failing in MIR borrowck.
+If this does invariant is broken MIR typeck ends up failing with an ICE.
+
+### Applying inference results from a goal does not change its result ❌
+
+TODO: this invariant is formulated in a weird way and needs to be elaborated.
+Pretty much: I would like this check to only fail if there's a solver bug:
+https://github.com/rust-lang/rust/blob/2ffeb4636b4ae376f716dc4378a7efb37632dc2d/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs#L391-L407
+
+If we prove some goal/equate types/whatever, apply the resulting inference constraints,
+and then redo the original action, the result should be the same.
+
+This unfortunately does not hold - at least in the new solver - due to a few annoying reasons.
+
+### The trait solver has to be *locally sound* ✅
+
+This means that we must never return *success* for goals for which no `impl` exists. That would
+mean we assume a trait is implemented even though it is not, which is very likely to result in
+actual unsoundness. When using `where`-bounds to prove a goal, the `impl` will be provided by the
+user of the item.
+
+This invariant only holds if we check region constraints. As we do not check region constraints
+during implicit negative overlap check in coherence, this invariant is broken there. As this check
+relies on *completeness* of the trait solver, it is not able to use the current region constraints
+check - `InferCtxt::resolve_regions` - as its handling of type outlives goals is incomplete.
+
+### Normalization of semantically equal aliases in empty environments results in a unique type ✅
+
+Normalization for alias types/consts has to have 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<T: Trait>(
+ x: <T as Trait>::Assoc
+) -> <T as Trait>::Assoc {
+ x
+}
+```
+
+Many of the currently known unsound issues end up relying on this invariant being broken.
+It is however very difficult to imagine a sound type system without this invariant, so
+the issue is that the invariant is broken, not that we incorrectly rely on it.
+
+### Generic goals and their instantiations 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 errors at that point.
+
+TODO: example for overflow error post-monomorphization
+
+This invariant is relied on to allow the normalization of generic aliases. Breaking
+it can easily result in unsoundness, e.g. [#57893](https://github.com/rust-lang/rust/issues/57893)
+
+### Trait goals in empty environments are proven by a unique impl ✅
+
+If a trait goal holds with an empty environment, there should be a unique `impl`,
+either user-defined or builtin, which is used to prove that goal. This is
+necessary to select a unique method. It
+
+We do however break this invariant in few cases, some of which are due to bugs,
+some by design:
+- *marker traits* are allowed to overlap as they do not have associated items
+- *specialization* allows specializing impls to overlap with their parent
+- the builtin trait object trait implementation can overlap with a user-defined impl:
+[#57893]
+
+### The type system is complete ❌
+
+The type system is not complete, it often adds unnecessary inference constraints, and errors
+even though the goal could hold.
+
+- method selection
+- opaque type inference
+- handling type outlives constraints
+- preferring `ParamEnv` candidates over `Impl` candidates during candidate selection
+in the trait solver
+
+#### The type system is complete during the implicit negative overlap check in coherence ✅
+
+During the implicit negative overlap check in coherence we must never return *error* for
+goals which can be proven. This would allow for overlapping impls with potentially different
+associated items, breaking a bunch of other invariants.
+
+This invariant is currently broken in many different ways while actually something we rely on.
+We have to be careful as it is quite easy to break:
+- generalization of aliases
+- generalization during subtyping binders (luckily not exploitable in coherence)
+
+### 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`.
+
+We also have to be careful with relying on equality of regions in the trait solver.
+This is fine for codegen, as we treat all erased regions as equal. We can however
+lose equality information from HIR to MIR typeck.
+
+The new solver "uniquifies regions" during canonicalization, canonicalizing `u32: Trait<'x, 'x>`
+as `exists<'0, '1> u32: Trait<'0, '1>`, to make it harder to rely on this property.
+
+### Removing ambiguity makes strictly more things compile ❌
+
+Ideally we *should* not rely on ambiguity for things to compile.
+Not doing that will cause future improvements to be breaking changes.
+
+Due to *incompleteness* this is not the case and improving inference can result in inference
+changes, breaking existing projects.
+
+### Semantic equality implies structural equality ✅
+
+Two types being equal in the type system must mean that they have the
+same `TypeId` after instantiating their generic parameters with concrete
+arguments. This currently does not hold: [#97156].
+
+[#57893]: https://github.com/rust-lang/rust/issues/57893
+[#97156]: https://github.com/rust-lang/rust/issues/97156
+[#114936]: https://github.com/rust-lang/rust/issues/114936 \ No newline at end of file
diff --git a/src/doc/rustc-dev-guide/src/solve/the-solver.md b/src/doc/rustc-dev-guide/src/solve/the-solver.md
index 61e6cad1c..f7d82d117 100644
--- a/src/doc/rustc-dev-guide/src/solve/the-solver.md
+++ b/src/doc/rustc-dev-guide/src/solve/the-solver.md
@@ -6,12 +6,71 @@ 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.
+## A rough walkthrough
-To deal with inference variables and to improve caching, we use
-[canonicalization](./canonicalization.md).
+The entry-point of the solver is `InferCtxtEvalExt::evaluate_root_goal`. This
+function sets up the root `EvalCtxt` and then calls `EvalCtxt::evaluate_goal`,
+to actually enter the trait solver.
-TODO: write the remaining code for this as well.
+`EvalCtxt::evaluate_goal` handles [canonicalization](./canonicalization.md), caching,
+overflow, and solver cycles. Once that is done, it creates a nested `EvalCtxt` with a
+separate local `InferCtxt` and calls `EvalCtxt::compute_goal`, which is responsible for the
+'actual solver behavior'. We match on the `PredicateKind`, delegating to a separate function
+for each one.
+
+For trait goals, such a `Vec<T>: Clone`, `EvalCtxt::compute_trait_goal` has
+to collect all the possible ways this goal can be proven via
+`EvalCtxt::assemble_and_evaluate_candidates`. Each candidate is handled in
+a separate "probe", to not leak inference constraints to the other candidates.
+We then try to merge the assembled candidates via `EvalCtxt::merge_candidates`.
+
+
+## Important concepts and design pattern
+
+### `EvalCtxt::add_goal`
+
+To prove nested goals, we don't directly call `EvalCtxt::compute_goal`, but instead
+add the goal to the `EvalCtxt` with `EvalCtxt::all_goal`. We then prove all nested
+goals together in either `EvalCtxt::try_evaluate_added_goals` or
+`EvalCtxt::evaluate_added_goals_and_make_canonical_response`. This allows us to handle
+inference constraints from later goals.
+
+E.g. if we have both `?x: Debug` and `(): ConstrainToU8<?x>` as nested goals,
+then proving `?x: Debug` is initially ambiguous, but after proving `(): ConstrainToU8<?x>`
+we constrain `?x` to `u8` and proving `u8: Debug` succeeds.
+
+### Matching on `TyKind`
+
+We lazily normalize types in the solver, so we always have to assume that any types
+and constants are potentially unnormalized. This means that matching on `TyKind` can easily
+be incorrect.
+
+We handle normalization in two different ways. When proving `Trait` goals when normalizing
+associated types, we separately assemble candidates depending on whether they structurally
+match the self type. Candidates which match on the self type are handled in
+`EvalCtxt::assemble_candidates_via_self_ty` which recurses via
+`EvalCtxt::assemble_candidates_after_normalizing_self_ty`, which normalizes the self type
+by one level. In all other cases we have to match on a `TyKind` we first use
+`EvalCtxt::try_normalize_ty` to normalize the type as much as possible.
+
+### Higher ranked goals
+
+In case the goal is higher-ranked, e.g. `for<'a> F: FnOnce(&'a ())`, `EvalCtxt::compute_goal`
+eagerly instantiates `'a` with a placeholder and then recursively proves
+`F: FnOnce(&'!a ())` as a nested goal.
+
+### Dealing with choice
+
+Some goals can be proven in multiple ways. In these cases we try each option in
+a separate "probe" and then attempt to merge the resulting responses by using
+`EvalCtxt::try_merge_responses`. If merging the responses fails, we use
+`EvalCtxt::flounder` instead, returning ambiguity. For some goals, we try
+incompletely prefer some choices over others in case `EvalCtxt::try_merge_responses`
+fails.
+
+## Learning more
+
+The solver should be fairly self-contained. I hope that the above information provides a
+good foundation when looking at the code itself. Please reach out on zulip if you get stuck
+while doing so or there are some quirks and design decisions which were unclear and deserve
+better comments or should be mentioned here.
diff --git a/src/doc/rustc-dev-guide/src/solve/trait-solving.md b/src/doc/rustc-dev-guide/src/solve/trait-solving.md
index c3089f4a8..7c1e0b684 100644
--- a/src/doc/rustc-dev-guide/src/solve/trait-solving.md
+++ b/src/doc/rustc-dev-guide/src/solve/trait-solving.md
@@ -39,77 +39,6 @@ which does not have any nested goals. Therefore `Vec<T>: 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<T: Trait>(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<T: Trait>(
- x: <T as Trait>::Assoc
-) -> <T as Trait>::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`.
-
-We also have to be careful with relying on equality of regions in the trait solver.
-This is fine for codegen, as we treat all erased regions as equal. We can however
-lose equality information from HIR to MIR typeck.
-
-### 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/traits/solve/struct.Goal.html
[`Predicate`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.Predicate.html
diff --git a/src/doc/rustc-dev-guide/src/stabilization_guide.md b/src/doc/rustc-dev-guide/src/stabilization_guide.md
index 001ed25a5..9bc70f65c 100644
--- a/src/doc/rustc-dev-guide/src/stabilization_guide.md
+++ b/src/doc/rustc-dev-guide/src/stabilization_guide.md
@@ -109,7 +109,7 @@ to stabilize, something like (this example is taken from
```rust,ignore
// pub(restricted) visibilities (RFC 1422)
-(active, pub_restricted, "CURRENT_RUSTC_VERSION", Some(32409)),
+(unstable, pub_restricted, "CURRENT_RUSTC_VERSION", Some(32409)),
```
The above line should be moved down to the area for "accepted"
diff --git a/src/doc/rustc-dev-guide/src/tests/headers.md b/src/doc/rustc-dev-guide/src/tests/headers.md
index f066dbbb5..fce2397e5 100644
--- a/src/doc/rustc-dev-guide/src/tests/headers.md
+++ b/src/doc/rustc-dev-guide/src/tests/headers.md
@@ -190,7 +190,7 @@ The following headers are generally available, and not specific to particular
test suites.
* `compile-flags` passes extra command-line args to the compiler,
- e.g. `compile-flags -g` which forces debuginfo to be enabled.
+ e.g. `// compile-flags: -g` which forces debuginfo to be enabled.
* `run-flags` passes extra args to the test if the test is to be executed.
* `edition` controls the edition the test should be compiled with
(defaults to 2015). Example usage: `// edition:2018`.
diff --git a/src/doc/rustc-dev-guide/src/traits/unsize.md b/src/doc/rustc-dev-guide/src/traits/unsize.md
new file mode 100644
index 000000000..b11760992
--- /dev/null
+++ b/src/doc/rustc-dev-guide/src/traits/unsize.md
@@ -0,0 +1,84 @@
+# [`CoerceUnsized`](https://doc.rust-lang.org/std/ops/trait.CoerceUnsized.html)
+
+`CoerceUnsized` is primarily concerned with data containers. When a struct
+(typically, a smart pointer) implements `CoerceUnsized`, that means that the
+data it points to is being unsized.
+
+Some implementors of `CoerceUnsized` include:
+* `&T`
+* `Arc<T>`
+* `Box<T>`
+
+This trait is (eventually) intended to be implemented by user-written smart
+pointers, and there are rules about when a type is allowed to implement
+`CoerceUnsized` that are explained in the trait's documentation.
+
+# [`Unsize`](https://doc.rust-lang.org/std/marker/trait.Unsize.html)
+
+To contrast, the `Unsize` trait is concerned the actual types that are allowed
+to be unsized.
+
+This is not intended to be implemented by users ever, since `Unsize` does not
+instruct the compiler (namely codegen) *how* to unsize a type, just whether it
+is allowed to be unsized. This is paired somewhat intimately with codegen
+which must understand how types are represented and unsized.
+
+## Primitive unsizing implementations
+
+Built-in implementations are provided for:
+* `T` -> `dyn Trait + 'a` when `T: Trait` (and `T: Sized + 'a`, and `Trait`
+ is object safe).
+* `[T; N]` -> `[T]`
+
+## Structural implementations
+
+There are two implementations of `Unsize` which can be thought of as
+structural:
+* `(A1, A2, .., An): Unsize<(A1, A2, .., U)>` given `An: Unsize<U>`, which
+ allows the tail field of a tuple to be unsized. This is gated behind the
+ [`unsized_tuple_coercion`] feature.
+* `Struct<.., Pi, .., Pj, ..>: Unsize<Struct<.., Ui, .., Uj, ..>>` given
+ `TailField<Pi, .., Pj>: Unsize<Ui, .. Uj>`, which allows the tail field of a
+ struct to be unsized if it is the only field that mentions generic parameters
+ `Pi`, .., `Pj` (which don't need to be contiguous).
+
+The rules for the latter implementation are slightly complicated, since they
+may allow more than one parameter to be changed (not necessarily unsized) and
+are best stated in terms of the tail field of the struct.
+
+[`unsized_tuple_coercion`]: https://doc.rust-lang.org/beta/unstable-book/language-features/unsized-tuple-coercion.html
+
+## Upcasting implementations
+
+Two things are called "upcasting" internally:
+1. True upcasting `dyn SubTrait` -> `dyn SuperTrait` (this also allows
+ dropping auto traits and adjusting lifetimes, as below).
+2. Dropping auto traits and adjusting the lifetimes of dyn trait
+ *without changing the principal[^1]*:
+ `dyn Trait + AutoTraits... + 'a` -> `dyn Trait + NewAutoTraits... + 'b`
+ when `AutoTraits` ⊇ `NewAutoTraits`, and `'a: 'b`.
+
+These may seem like different operations, since (1.) includes adjusting the
+vtable of a dyn trait, while (2.) is a no-op. However, to the type system,
+these are handled with much the same code.
+
+This built-in implementation of `Unsize` is the most involved, particularly
+after [it was reworked](https://github.com/rust-lang/rust/pull/114036) to
+support the complexities of associated types.
+
+Specifically, the upcasting algorithm involves: For each supertrait of the
+source dyn trait's principal (including itself)...
+1. Unify the super trait ref with the principal of the target (making sure
+ we only ever upcast to a true supertrait, and never [via an impl]).
+2. For every auto trait in the source, check that it's present in the principal
+ (allowing us to drop auto traits, but never gain new ones).
+3. For every projection in the source, check that it unifies with a single
+ projection in the target (since there may be more than one given
+ `trait Sub: Sup<.., A = i32> + Sup<.., A = u32>`).
+
+[via an impl]: https://github.com/rust-lang/rust/blob/f3457dbf84cd86d284454d12705861398ece76c3/tests/ui/traits/trait-upcasting/illegal-upcast-from-impl.rs#L19
+
+Specifically, (3.) prevents a choice of projection bound to guide inference
+unnecessarily, though it may guide inference when it is unambiguous.
+
+[^1]: The principal is the one non-auto trait of a `dyn Trait`. \ No newline at end of file
diff --git a/src/doc/rustc/src/SUMMARY.md b/src/doc/rustc/src/SUMMARY.md
index 5c6633864..1b27b77b3 100644
--- a/src/doc/rustc/src/SUMMARY.md
+++ b/src/doc/rustc/src/SUMMARY.md
@@ -33,7 +33,7 @@
- [\*-esp-espidf](platform-support/esp-idf.md)
- [\*-unknown-fuchsia](platform-support/fuchsia.md)
- [\*-kmc-solid_\*](platform-support/kmc-solid.md)
- - [csky-unknown-linux-gnuabiv2](platform-support/csky-unknown-linux-gnuabiv2.md)
+ - [csky-unknown-linux-gnuabiv2\*](platform-support/csky-unknown-linux-gnuabiv2.md)
- [loongarch\*-unknown-linux-\*](platform-support/loongarch-linux.md)
- [loongarch\*-unknown-none\*](platform-support/loongarch-none.md)
- [m68k-unknown-linux-gnu](platform-support/m68k-unknown-linux-gnu.md)
@@ -41,6 +41,7 @@
- [mipsel-sony-psx](platform-support/mipsel-sony-psx.md)
- [mipsisa\*r6\*-unknown-linux-gnu\*](platform-support/mips-release-6.md)
- [nvptx64-nvidia-cuda](platform-support/nvptx64-nvidia-cuda.md)
+ - [powerpc64-ibm-aix](platform-support/aix.md)
- [riscv32imac-unknown-xous-elf](platform-support/riscv32imac-unknown-xous-elf.md)
- [sparc-unknown-none-elf](./platform-support/sparc-unknown-none-elf.md)
- [*-pc-windows-gnullvm](platform-support/pc-windows-gnullvm.md)
diff --git a/src/doc/rustc/src/codegen-options/index.md b/src/doc/rustc/src/codegen-options/index.md
index d72543c7e..cfbe1e09c 100644
--- a/src/doc/rustc/src/codegen-options/index.md
+++ b/src/doc/rustc/src/codegen-options/index.md
@@ -249,11 +249,9 @@ flavor. Valid options are:
* `gcc`: use the `cc` executable, which is typically gcc or clang on many systems.
* `ld`: use the `ld` executable.
* `msvc`: use the `link.exe` executable from Microsoft Visual Studio MSVC.
-* `ptx-linker`: use
- [`rust-ptx-linker`](https://github.com/denzp/rust-ptx-linker) for Nvidia
- NVPTX GPGPU support.
-* `bpf-linker`: use
- [`bpf-linker`](https://github.com/alessandrod/bpf-linker) for eBPF support.
+* `ptx`: use [`rust-ptx-linker`](https://github.com/denzp/rust-ptx-linker)
+ for Nvidia NVPTX GPGPU support.
+* `bpf`: use [`bpf-linker`](https://github.com/alessandrod/bpf-linker) for eBPF support.
* `wasm-ld`: use the [`wasm-ld`](https://lld.llvm.org/WebAssembly.html)
executable, a port of LLVM `lld` for WebAssembly.
* `ld64.lld`: use the LLVM `lld` executable with the [`-flavor darwin`
diff --git a/src/doc/rustc/src/exploit-mitigations.md b/src/doc/rustc/src/exploit-mitigations.md
index 172048704..d4e2fc52e 100644
--- a/src/doc/rustc/src/exploit-mitigations.md
+++ b/src/doc/rustc/src/exploit-mitigations.md
@@ -1,12 +1,12 @@
# Exploit Mitigations
-This chapter documents the exploit mitigations supported by the Rust
-compiler, and is by no means an extensive survey of the Rust programming
-language’s security features.
+This chapter documents the exploit mitigations supported by the Rust compiler,
+and is by no means an extensive survey of the Rust programming language’s
+security features.
This chapter is for software engineers working with the Rust programming
-language, and assumes prior knowledge of the Rust programming language and
-its toolchain.
+language, and assumes prior knowledge of the Rust programming language and its
+toolchain.
## Introduction
@@ -14,8 +14,8 @@ its toolchain.
The Rust programming language provides memory[1] and thread[2] safety
guarantees via its ownership[3], references and borrowing[4], and slice
types[5] features. However, Unsafe Rust[6] introduces unsafe blocks, unsafe
-functions and methods, unsafe traits, and new types that are not subject to
-the borrowing rules.
+functions and methods, unsafe traits, and new types that are not subject to the
+borrowing rules.
Parts of the Rust standard library are implemented as safe abstractions over
unsafe code (and historically have been vulnerable to memory corruption[7]).
@@ -23,33 +23,32 @@ Furthermore, the Rust code and documentation encourage creating safe
abstractions over unsafe code. This can cause a false sense of security if
unsafe code is not properly reviewed and tested.
-Unsafe Rust introduces features that do not provide the same memory and
-thread safety guarantees. This causes programs or libraries to be
-susceptible to memory corruption (CWE-119)[8] and concurrency issues
-(CWE-557)[9]. Modern C and C++ compilers provide exploit mitigations to
-increase the difficulty to exploit vulnerabilities resulting from these
-issues. Therefore, the Rust compiler must also support these exploit
-mitigations in order to mitigate vulnerabilities resulting from the use of
-Unsafe Rust. This chapter documents these exploit mitigations and how they
-apply to Rust.
+Unsafe Rust introduces features that do not provide the same memory and thread
+safety guarantees. This causes programs or libraries to be susceptible to
+memory corruption (CWE-119)[8] and concurrency issues (CWE-557)[9]. Modern C
+and C++ compilers provide exploit mitigations to increase the difficulty to
+exploit vulnerabilities resulting from these issues. Therefore, the Rust
+compiler must also support these exploit mitigations in order to mitigate
+vulnerabilities resulting from the use of Unsafe Rust. This chapter documents
+these exploit mitigations and how they apply to Rust.
-This chapter does not discuss the effectiveness of these exploit mitigations
-as they vary greatly depending on several factors besides their design and
-implementation, but rather describe what they do, so their effectiveness can
-be understood within a given context.
+This chapter does not discuss the effectiveness of these exploit mitigations as
+they vary greatly depending on several factors besides their design and
+implementation, but rather describe what they do, so their effectiveness can be
+understood within a given context.
## Exploit mitigations
-This section documents the exploit mitigations applicable to the Rust
-compiler when building programs for the Linux operating system on the AMD64
-architecture and equivalent.<sup id="fnref:1" role="doc-noteref"><a
-href="#fn:1" class="footnote">1</a></sup>
+This section documents the exploit mitigations applicable to the Rust compiler
+when building programs for the Linux operating system on the AMD64 architecture
+and equivalent.<sup id="fnref:1" role="doc-noteref"><a href="#fn:1"
+class="footnote">1</a></sup> All examples in this section were built using
+nightly builds of the Rust compiler on Debian testing.
-The Rust Programming Language currently has no specification. The Rust
-compiler (i.e., rustc) is the language reference implementation. All
-references to “the Rust compiler” in this chapter refer to the language
-reference implementation.
+The Rust Programming Language currently has no specification. The Rust compiler
+(i.e., rustc) is the language reference implementation. All references to “the
+Rust compiler” in this chapter refer to the language reference implementation.
Table I \
Summary of exploit mitigations supported by the Rust compiler when building
@@ -83,8 +82,8 @@ instructing the dynamic linker to load it similarly to a shared object at a
random load address, thus also benefiting from address-space layout
randomization (ASLR). This is also referred to as “full ASLR”.
-The Rust compiler supports position-independent executable, and enables it
-by default since version 0.12.0 (2014-10-09)[10]–[13].
+The Rust compiler supports position-independent executable, and enables it by
+default since version 0.12.0 (2014-10-09)[10]–[13].
```text
$ readelf -h target/release/hello-rust | grep Type:
@@ -93,8 +92,7 @@ $ readelf -h target/release/hello-rust | grep Type:
Fig. 1. Checking if an executable is a position-independent executable.
An executable with an object type of `ET_DYN` (i.e., shared object) and not
-`ET_EXEC` (i.e., executable) is a position-independent executable (see Fig.
-1).
+`ET_EXEC` (i.e., executable) is a position-independent executable (see Fig. 1).
### Integer overflow checks
@@ -104,8 +102,11 @@ behavior (which may cause vulnerabilities) by checking for results of signed
and unsigned integer computations that cannot be represented in their type,
resulting in an overflow or wraparound.
-The Rust compiler supports integer overflow checks, and enables it when
-debug assertions are enabled since version 1.1.0 (2015-06-25)[14]–[20].
+The Rust compiler supports integer overflow checks, and enables it when debug
+assertions are enabled since version 1.0.0 (2015-05-15)[14]–[17], but support
+for it was not completed until version 1.1.0 (2015-06-25)[16]. An option to
+control integer overflow checks was later stabilized in version 1.17.0
+(2017-04-27)[18]–[20].
```compile_fail
fn main() {
@@ -136,21 +137,21 @@ u: 0
Fig. 4. Build and execution of hello-rust-integer with debug assertions
disabled.
-Integer overflow checks are enabled when debug assertions are enabled (see
-Fig. 3), and disabled when debug assertions are disabled (see Fig. 4). To
-enable integer overflow checks independently, use the option to control
-integer overflow checks, scoped attributes, or explicit checking methods
-such as `checked_add`<sup id="fnref:2" role="doc-noteref"><a href="#fn:2"
+Integer overflow checks are enabled when debug assertions are enabled (see Fig.
+3), and disabled when debug assertions are disabled (see Fig. 4). To enable
+integer overflow checks independently, use the option to control integer
+overflow checks, scoped attributes, or explicit checking methods such as
+`checked_add`<sup id="fnref:2" role="doc-noteref"><a href="#fn:2"
class="footnote">2</a></sup>.
-It is recommended that explicit wrapping methods such as `wrapping_add` be
-used when wrapping semantics are intended, and that explicit checking and
-wrapping methods always be used when using Unsafe Rust.
+It is recommended that explicit wrapping methods such as `wrapping_add` be used
+when wrapping semantics are intended, and that explicit checking and wrapping
+methods always be used when using Unsafe Rust.
-<small id="fn:2">2\. See [the `u32` docs](../std/primitive.u32.html)
-for more information on the checked, overflowing, saturating, and wrapping
-methods (using u32 as an example). <a href="#fnref:2"
-class="reversefootnote" role="doc-backlink">↩</a></small>
+<small id="fn:2">2\. See [the `u32` docs](../std/primitive.u32.html) for more
+information on the checked, overflowing, saturating, and wrapping methods
+(using u32 as an example). <a href="#fnref:2" class="reversefootnote"
+role="doc-backlink">↩</a></small>
### Non-executable memory regions
@@ -158,17 +159,16 @@ class="reversefootnote" role="doc-backlink">↩</a></small>
Non-executable memory regions increase the difficulty of exploitation by
limiting the memory regions that can be used to execute arbitrary code. Most
modern processors provide support for the operating system to mark memory
-regions as non executable, but it was previously emulated by software, such
-as in grsecurity/PaX's
-[PAGEEXEC](https://pax.grsecurity.net/docs/pageexec.txt) and
-[SEGMEXEC](https://pax.grsecurity.net/docs/segmexec.txt), on processors that
-did not provide support for it. This is also known as “No Execute (NX) Bit”,
-“Execute Disable (XD) Bit”, “Execute Never (XN) Bit”, and others.
+regions as non executable, but it was previously emulated by software, such as
+in grsecurity/PaX’s [PAGEEXEC](https://pax.grsecurity.net/docs/pageexec.txt)
+and [SEGMEXEC](https://pax.grsecurity.net/docs/segmexec.txt), on processors
+that did not provide support for it. This is also known as “No Execute (NX)
+Bit”, “Execute Disable (XD) Bit”, “Execute Never (XN) Bit”, and others.
The Rust compiler supports non-executable memory regions, and enables it by
-default since its initial release, version 0.1 (2012-01-20)[21], [22], but
-has regressed since then[23]–[25], and enforced by default since version
-1.8.0 (2016-04-14)[25].
+default since its initial release, version 0.1 (2012-01-20)[21], [22], but has
+regressed since then[23]–[25], and enforced by default since version 1.8.0
+(2016-04-14)[25].
```text
$ readelf -l target/release/hello-rust | grep -A 1 GNU_STACK
@@ -178,9 +178,9 @@ $ readelf -l target/release/hello-rust | grep -A 1 GNU_STACK
Fig. 5. Checking if non-executable memory regions are enabled for a given
binary.
-The presence of an element of type `PT_GNU_STACK` in the program header
-table with the `PF_X` (i.e., executable) flag unset indicates non-executable
-memory regions<sup id="fnref:3" role="doc-noteref"><a href="#fn:3"
+The presence of an element of type `PT_GNU_STACK` in the program header table
+with the `PF_X` (i.e., executable) flag unset indicates non-executable memory
+regions<sup id="fnref:3" role="doc-noteref"><a href="#fn:3"
class="footnote">3</a></sup> are enabled for a given binary (see Fig. 5).
Conversely, the presence of an element of type `PT_GNU_STACK` in the program
header table with the `PF_X` flag set or the absence of an element of type
@@ -196,38 +196,40 @@ class="reversefootnote" role="doc-backlink">↩</a></small>
Stack clashing protection protects the stack from overlapping with another
memory region—allowing arbitrary data in both to be overwritten using each
-other—by reading from the stack pages as the stack grows to cause a page
-fault when attempting to read from the guard page/region. This is also
-referred to as “stack probes” or “stack probing”.
+other—by reading from the stack pages as the stack grows to cause a page fault
+when attempting to read from the guard page/region. This is also referred to as
+“stack probes” or “stack probing”.
The Rust compiler supports stack clashing protection via stack probing, and
enables it by default since version 1.20.0 (2017-08-31)[26]–[29].
-![Screenshot of IDA Pro listing cross references to __rust_probestack in hello-rust.](images/image1.png "Cross references to __rust_probestack in hello-rust.")
-Fig. 6. IDA Pro listing cross references to `__rust_probestack` in
-hello-rust.
-
```rust
-fn hello() {
- println!("Hello, world!");
+fn main() {
+ let v: [u8; 16384] = [1; 16384];
+ let first = &v[0];
+ println!("The first element is: {first}");
}
+```
+Fig. 6. hello-rust-stack-probe-1 program.
+![Screenshot of IDA Pro listing the "unrolled loop" stack probe variant in modified hello-rust.](images/image1.png "The \"unrolled loop\" stack probe variant in modified hello-rust.")
+Fig. 7. The "unrolled loop" stack probe variant in modified hello-rust.
+
+```rust
fn main() {
- let _: [u64; 1024] = [0; 1024];
- hello();
+ let v: [u8; 65536] = [1; 65536];
+ let first = &v[0];
+ println!("The first element is: {first}");
}
```
-Fig 7. Modified hello-rust.
+Fig. 8. hello-rust-stack-probe-2 program.
-![Screenshot of IDA Pro listing cross references to __rust_probestack in modified hello-rust.](images/image2.png "Cross references to __rust_probestack in modified hello-rust.")
-Fig. 8. IDA Pro listing cross references to `__rust_probestack` in modified
-hello-rust.
+![Screenshot of IDA Pro listing the "standard loop" stack probe variant in modified hello-rust.](images/image2.png "The \"standard loop\" stack probe variant in modified hello-rust.")
+Fig. 9. The "standard loop" stack probe variant in modified hello-rust.
-To check if stack clashing protection is enabled for a given binary, search
-for cross references to `__rust_probestack`. The `__rust_probestack` is
-called in the prologue of functions whose stack size is larger than a page
-size (see Fig. 6), and can be forced for illustration purposes by modifying
-the hello-rust example as seen in Fig. 7 and Fig. 8.
+To check if stack clashing protection is enabled for a given binary, look for
+any of the two stack probe variants in the prologue of functions whose stack
+size is larger than a page size (see Figs. 6–9).
### Read-only relocations and immediate binding
@@ -246,21 +248,20 @@ $ readelf -l target/release/hello-rust | grep GNU_RELRO
```
Fig. 9. Checking if read-only relocations is enabled for a given binary.
-The presence of an element of type `PT_GNU_RELRO` in the program header
-table indicates read-only relocations are enabled for a given binary (see
-Fig. 9). Conversely, the absence of an element of type `PT_GNU_RELRO` in the
-program header table indicates read-only relocations are not enabled for a
-given binary.
+The presence of an element of type `PT_GNU_RELRO` in the program header table
+indicates read-only relocations are enabled for a given binary (see Fig. 9).
+Conversely, the absence of an element of type `PT_GNU_RELRO` in the program
+header table indicates read-only relocations are not enabled for a given
+binary.
**Immediate binding** protects additional segments containing relocations
-(i.e., `.got.plt`) from being overwritten by instructing the dynamic linker
-to perform all relocations before transferring control to the program during
-startup, so all segments containing relocations can be marked read only
-(when combined with read-only relocations). This is also referred to as
-“full RELRO”.
+(i.e., `.got.plt`) from being overwritten by instructing the dynamic linker to
+perform all relocations before transferring control to the program during
+startup, so all segments containing relocations can be marked read only (when
+combined with read-only relocations). This is also referred to as “full RELRO”.
-The Rust compiler supports immediate binding, and enables it by default
-since version 1.21.0 (2017-10-12)[30], [31].
+The Rust compiler supports immediate binding, and enables it by default since
+version 1.21.0 (2017-10-12)[30], [31].
```text
$ readelf -d target/release/hello-rust | grep BIND_NOW
@@ -270,16 +271,15 @@ Fig. 10. Checking if immediate binding is enabled for a given binary.
The presence of an element with the `DT_BIND_NOW` tag and the `DF_BIND_NOW`
flag<sup id="fnref:4" role="doc-noteref"><a href="#fn:4"
-class="footnote">4</a></sup> in the dynamic section indicates immediate
-binding is enabled for a given binary (see Fig. 10). Conversely, the absence
-of an element with the `DT_BIND_NOW` tag and the `DF_BIND_NOW` flag in the
-dynamic section indicates immediate binding is not enabled for a given
-binary.
+class="footnote">4</a></sup> in the dynamic section indicates immediate binding
+is enabled for a given binary (see Fig. 10). Conversely, the absence of an
+element with the `DT_BIND_NOW` tag and the `DF_BIND_NOW` flag in the dynamic
+section indicates immediate binding is not enabled for a given binary.
The presence of both an element of type `PT_GNU_RELRO` in the program header
-table and of an element with the `DT_BIND_NOW` tag and the `DF_BIND_NOW`
-flag in the dynamic section indicates full RELRO is enabled for a given
-binary (see Fig. 9 and Fig. 10).
+table and of an element with the `DT_BIND_NOW` tag and the `DF_BIND_NOW` flag
+in the dynamic section indicates full RELRO is enabled for a given binary (see
+Figs. 9–10).
<small id="fn:4">4\. And the `DF_1_NOW` flag for some link editors. <a
href="#fnref:4" class="reversefootnote" role="doc-backlink">↩</a></small>
@@ -287,26 +287,24 @@ href="#fnref:4" class="reversefootnote" role="doc-backlink">↩</a></small>
### Heap corruption protection
-Heap corruption protection protects memory allocated dynamically by
-performing several checks, such as checks for corrupted links between list
-elements, invalid pointers, invalid sizes, double/multiple “frees” of the
-same memory allocated, and many corner cases of these. These checks are
-implementation specific, and vary per allocator.
+Heap corruption protection protects memory allocated dynamically by performing
+several checks, such as checks for corrupted links between list elements,
+invalid pointers, invalid sizes, double/multiple “frees” of the same memory
+allocated, and many corner cases of these. These checks are implementation
+specific, and vary per allocator.
[ARM Memory Tagging Extension
(MTE)](https://community.arm.com/developer/ip-products/processors/b/processors-ip-blog/posts/enhancing-memory-safety),
-when available, will provide hardware assistance for a probabilistic
-mitigation to detect memory safety violations by tagging memory allocations,
-and automatically checking that the correct tag is used on every memory
-access.
+when available, will provide hardware assistance for a probabilistic mitigation
+to detect memory safety violations by tagging memory allocations, and
+automatically checking that the correct tag is used on every memory access.
Rust’s default allocator has historically been
-[jemalloc](http://jemalloc.net/), and it has long been the cause of issues
-and the subject of much discussion[32]–[38]. Consequently, it has been
-removed as the default allocator in favor of the operating system’s standard
-C library default allocator<sup id="fnref:5" role="doc-noteref"><a
-href="#fn:5" class="footnote">5</a></sup> since version 1.32.0
-(2019-01-17)[39].
+[jemalloc](http://jemalloc.net/), and it has long been the cause of issues and
+the subject of much discussion[32]–[38]. Consequently, it has been removed as
+the default allocator in favor of the operating system’s standard C library
+default allocator<sup id="fnref:5" role="doc-noteref"><a href="#fn:5"
+class="footnote">5</a></sup> since version 1.32.0 (2019-01-17)[39].
```rust,no_run
fn main() {
@@ -330,8 +328,7 @@ $ cargo run
free(): invalid next size (normal)
Aborted
```
-Fig. 12. Build and execution of hello-rust-heap with debug assertions
-enabled.
+Fig. 12. Build and execution of hello-rust-heap with debug assertions enabled.
```text
$ cargo run --release
@@ -341,47 +338,41 @@ $ cargo run --release
free(): invalid next size (normal)
Aborted
```
-Fig. 13. Build and execution of hello-rust-heap with debug assertions
-disabled.
+Fig. 13. Build and execution of hello-rust-heap with debug assertions disabled.
-Heap corruption checks are being performed when using the default allocator
-(i.e., the GNU Allocator) as seen in Fig. 12 and Fig. 13.
+Heap corruption checks are performed when using the default allocator (i.e.,
+the GNU Allocator) (see Figs. 12–13).
<small id="fn:5">5\. Linux's standard C library default allocator is the GNU
-Allocator, which is derived from ptmalloc (pthreads malloc) by Wolfram
-Gloger, which in turn is derived from dlmalloc (Doug Lea malloc) by Doug
-Lea. <a href="#fnref:5" class="reversefootnote"
-role="doc-backlink">↩</a></small>
+Allocator, which is derived from ptmalloc (pthreads malloc) by Wolfram Gloger,
+which in turn is derived from dlmalloc (Doug Lea malloc) by Doug Lea. <a
+href="#fnref:5" class="reversefootnote" role="doc-backlink">↩</a></small>
### Stack smashing protection
-Stack smashing protection protects programs from stack-based buffer
-overflows by inserting a random guard value between local variables and the
-saved return instruction pointer, and checking if this value has changed
-when returning from a function. This is also known as “Stack Protector” or
-“Stack Smashing Protector (SSP)”.
+Stack smashing protection protects programs from stack-based buffer overflows
+by inserting a random guard value between local variables and the saved return
+instruction pointer, and checking if this value has changed when returning from
+a function. This is also known as “Stack Protector” or “Stack Smashing
+Protector (SSP)”.
-The Rust compiler supports stack smashing protection on nightly builds[42].
+The Rust compiler supports stack smashing protection on nightly builds[40].
![Screenshot of IDA Pro listing cross references to __stack_chk_fail in hello-rust.](images/image3.png "Cross references to __stack_chk_fail in hello-rust.")
-Fig. 14. IDA Pro listing cross references to `__stack_chk_fail` in
-hello-rust.
+Fig. 14. IDA Pro listing cross references to `__stack_chk_fail` in hello-rust.
-To check if stack smashing protection is enabled for a given binary, search
-for cross references to `__stack_chk_fail`. The presence of these
-cross-references in Rust-compiled code (e.g., `hello_rust::main`) indicates
-that the stack smashing protection is enabled (see Fig. 14).
+To check if stack smashing protection is enabled for a given binary, search for
+cross references to `__stack_chk_fail` (see Fig. 14).
### Forward-edge control flow protection
-Forward-edge control flow protection protects programs from having its
-control flow changed/hijacked by performing checks to ensure that
-destinations of indirect branches are one of their valid destinations in the
-control flow graph. The comprehensiveness of these checks vary per
-implementation. This is also known as “forward-edge control flow integrity
-(CFI)”.
+Forward-edge control flow protection protects programs from having its control
+flow changed/hijacked by performing checks to ensure that destinations of
+indirect branches are one of their valid destinations in the control flow
+graph. The comprehensiveness of these checks vary per implementation. This is
+also known as “forward-edge control flow integrity (CFI)”.
Newer processors provide hardware assistance for forward-edge control flow
protection, such as ARM Branch Target Identification (BTI), ARM Pointer
@@ -394,22 +385,19 @@ commercially available [grsecurity/PaX Reuse Attack Protector
(RAP)](https://grsecurity.net/rap_faq).
The Rust compiler supports forward-edge control flow protection on nightly
-builds[40]-[41] <sup id="fnref:6" role="doc-noteref"><a href="#fn:6"
+builds[41]-[42] <sup id="fnref:6" role="doc-noteref"><a href="#fn:6"
class="footnote">6</a></sup>.
```text
-$ readelf -s -W target/debug/rust-cfi | grep "\.cfi"
- 12: 0000000000005170 46 FUNC LOCAL DEFAULT 14 _RNvCsjaOHoaNjor6_8rust_cfi7add_one.cfi
- 15: 00000000000051a0 16 FUNC LOCAL DEFAULT 14 _RNvCsjaOHoaNjor6_8rust_cfi7add_two.cfi
- 17: 0000000000005270 396 FUNC LOCAL DEFAULT 14 _RNvCsjaOHoaNjor6_8rust_cfi4main.cfi
-...
+$ readelf -s -W target/release/hello-rust | grep "\.cfi"
+ 5: 0000000000006480 657 FUNC LOCAL DEFAULT 15 _ZN10hello_rust4main17h4e359f1dcd627c83E.cfi
```
-Fig. 15. Checking if LLVM CFI is enabled for a given binary[41].
+Fig. 15. Checking if LLVM CFI is enabled for a given binary.
The presence of symbols suffixed with ".cfi" or the `__cfi_init` symbol (and
-references to `__cfi_check`) indicates that LLVM CFI (i.e., forward-edge control
-flow protection) is enabled for a given binary. Conversely, the absence of
-symbols suffixed with ".cfi" or the `__cfi_init` symbol (and references to
+references to `__cfi_check`) indicates that LLVM CFI (i.e., forward-edge
+control flow protection) is enabled for a given binary. Conversely, the absence
+of symbols suffixed with ".cfi" or the `__cfi_init` symbol (and references to
`__cfi_check`) indicates that LLVM CFI is not enabled for a given binary (see
Fig. 15).
@@ -421,48 +409,47 @@ class="reversefootnote" role="doc-backlink">↩</a></small>
### Backward-edge control flow protection
**Shadow stack** protects saved return instruction pointers from being
-overwritten by storing a copy of them on a separate (shadow) stack, and
-using these copies as authoritative values when returning from functions.
-This is also known as “ShadowCallStack” and “Return Flow Guard”, and is
-considered an implementation of backward-edge control flow protection (or
-“backward-edge CFI”).
+overwritten by storing a copy of them on a separate (shadow) stack, and using
+these copies as authoritative values when returning from functions. This is
+also known as “ShadowCallStack” and “Return Flow Guard”, and is considered an
+implementation of backward-edge control flow protection (or “backward-edge
+CFI”).
**Safe stack** protects not only the saved return instruction pointers, but
-also register spills and some local variables from being overwritten by
-storing unsafe variables, such as large arrays, on a separate (unsafe)
-stack, and using these unsafe variables on the separate stack instead. This
-is also known as “SafeStack”, and is also considered an implementation of
-backward-edge control flow protection.
+also register spills and some local variables from being overwritten by storing
+unsafe variables, such as large arrays, on a separate (unsafe) stack, and using
+these unsafe variables on the separate stack instead. This is also known as
+“SafeStack”, and is also considered an implementation of backward-edge control
+flow protection.
-Both shadow and safe stack are intended to be a more comprehensive
-alternatives to stack smashing protection as they protect the saved return
-instruction pointers (and other data in the case of safe stack) from
-arbitrary writes and non-linear out-of-bounds writes.
+Both shadow and safe stack are intended to be a more comprehensive alternatives
+to stack smashing protection as they protect the saved return instruction
+pointers (and other data in the case of safe stack) from arbitrary writes and
+non-linear out-of-bounds writes.
Newer processors provide hardware assistance for backward-edge control flow
-protection, such as ARM Pointer Authentication, and Intel Shadow Stack as
-part of Intel CET.
+protection, such as ARM Pointer Authentication, and Intel Shadow Stack as part
+of Intel CET.
-The Rust compiler supports shadow stack for aarch64 only
-<sup id="fnref:7" role="doc-noteref"><a href="#fn:7" class="footnote">7</a></sup>
-on nightly Rust compilers [43]-[44]. Safe stack is available on nightly
-Rust compilers [45]-[46].
+The Rust compiler supports shadow stack for the AArch64 architecture<sup
+id="fnref:7" role="doc-noteref"><a href="#fn:7" class="footnote">7</a></sup>on
+nightly builds[43]-[44], and also supports safe stack on nightly
+builds[45]-[46].
```text
$ readelf -s target/release/hello-rust | grep __safestack_init
- 1177: 00000000000057b0 444 FUNC GLOBAL DEFAULT 9 __safestack_init
+ 678: 0000000000008c80 426 FUNC GLOBAL DEFAULT 15 __safestack_init
```
Fig. 16. Checking if LLVM SafeStack is enabled for a given binary.
-The presence of the `__safestack_init` symbol indicates that LLVM SafeStack
-is enabled for a given binary (see Fig. 16). Conversely, the absence of the
-`__safestack_init` symbol indicates that LLVM SafeStack is not enabled for a
-given binary.
+The presence of the `__safestack_init` symbol indicates that LLVM SafeStack is
+enabled for a given binary. Conversely, the absence of the `__safestack_init`
+symbol indicates that LLVM SafeStack is not enabled for a given binary (see
+Fig. 16).
-<small id="fn:7">7\. The shadow stack implementation for the AMD64
-architecture and equivalent in LLVM was removed due to performance and
-security issues. <a href="#fnref:7" class="reversefootnote"
-role="doc-backlink">↩</a></small>
+<small id="fn:7">7\. The shadow stack implementation for the AMD64 architecture
+and equivalent in LLVM was removed due to performance and security issues. <a
+href="#fnref:7" class="reversefootnote" role="doc-backlink">↩</a></small>
## Appendix
@@ -470,29 +457,28 @@ role="doc-backlink">↩</a></small>
As of the latest version of the [Linux Standard Base (LSB) Core
Specification](https://refspecs.linuxfoundation.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/progheader.html),
the `PT_GNU_STACK` program header indicates whether the stack should be
-executable, and the absence of this header indicates that the stack should
-be executable. However, the Linux kernel currently sets the
-`READ_IMPLIES_EXEC` personality upon loading any executable with the
-`PT_GNU_STACK` program header and the `PF_X `flag set or with the absence of
-this header, resulting in not only the stack, but also all readable virtual
-memory mappings being executable.
+executable, and the absence of this header indicates that the stack should be
+executable. However, the Linux kernel currently sets the `READ_IMPLIES_EXEC`
+personality upon loading any executable with the `PT_GNU_STACK` program header
+and the `PF_X` flag set or with the absence of this header, resulting in not
+only the stack, but also all readable virtual memory mappings being executable.
An attempt to fix this [was made in
2012](https://lore.kernel.org/lkml/f298f914-2239-44e4-8aa1-a51282e7fac0@zmail15.collab.prod.int.phx2.redhat.com/),
and another [was made in
2020](https://lore.kernel.org/kernel-hardening/20200327064820.12602-1-keescook@chromium.org/).
The former never landed, and the latter partially fixed it, but introduced
-other issues—the absence of the `PT_GNU_STACK` program header still causes
-not only the stack, but also all readable virtual memory mappings to be
-executable in some architectures, such as IA-32 and equivalent (or causes
-the stack to be non-executable in some architectures, such as AMD64 and
-equivalent, contradicting the LSB).
+other issues—the absence of the `PT_GNU_STACK` program header still causes not
+only the stack, but also all readable virtual memory mappings to be executable
+in some architectures, such as IA-32 and equivalent (or causes the stack to be
+non-executable in some architectures, such as AMD64 and equivalent,
+contradicting the LSB).
-The `READ_IMPLIES_EXEC` personality needs to be completely separated from
-the `PT_GNU_STACK` program header by having a separate option for it (or
-setarch -X could just be used whenever `READ_IMPLIES_EXEC` is needed), and
-the absence of the `PT_GNU_STACK` program header needs to have more secure
-defaults (unrelated to `READ_IMPLIES_EXEC`).
+The `READ_IMPLIES_EXEC` personality needs to be completely separated from the
+`PT_GNU_STACK` program header by having a separate option for it (or setarch -X
+could just be used whenever `READ_IMPLIES_EXEC` is needed), and the absence of
+the `PT_GNU_STACK` program header needs to have more secure defaults (unrelated
+to `READ_IMPLIES_EXEC`).
## References
@@ -576,19 +562,19 @@ defaults (unrelated to `READ_IMPLIES_EXEC`).
25. A. Clark. “Explicitly disable stack execution on linux and bsd #30859.”
GitHub. <https://github.com/rust-lang/rust/pull/30859>.
-26. “Replace stack overflow checking with stack probes #16012.” GitHub.
+26. Zoxc. “Replace stack overflow checking with stack probes #16012.” GitHub.
<https://github.com/rust-lang/rust/issues/16012>.
-27. B. Striegel. “Extend stack probe support to non-tier-1 platforms, and
- clarify policy for mitigating LLVM-dependent unsafety #43241.” GitHub.
- <https://github.com/rust-lang/rust/issues/43241>.
-
-28. A. Crichton. “rustc: Implement stack probes for x86 #42816.” GitHub.
+27. A. Crichton. “rustc: Implement stack probes for x86 #42816.” GitHub.
<https://github.com/rust-lang/rust/pull/42816>.
-29. A. Crichton. “Add \_\_rust\_probestack intrinsic #175.” GitHub.
+28. A. Crichton. “Add \_\_rust\_probestack intrinsic #175.” GitHub.
<https://github.com/rust-lang/compiler-builtins/pull/175>.
+29. S. Guelton, S. Ledru, J. Stone. “Bringing Stack Clash Protection to Clang /
+ X86 — the Open Source Way.” The LLVM Project Blog.
+ <https://blog.llvm.org/posts/2021-01-05-stack-clash-protection/>.
+
30. B. Anderson. “Consider applying -Wl,-z,relro or -Wl,-z,relro,-z,now by
default #29877.” GitHub. <https://github.com/rust-lang/rust/issues/29877>.
@@ -621,16 +607,16 @@ defaults (unrelated to `READ_IMPLIES_EXEC`).
39. A. Crichton. “Remove the alloc\_jemalloc crate #55238.” GitHub.
<https://github.com/rust-lang/rust/pull/55238>.
-40. R. de C Valle. “Tracking Issue for LLVM Control Flow Integrity (CFI) Support
+40. bbjornse. “Add codegen option for using LLVM stack smash protection #84197.”
+ GitHub. <https://github.com/rust-lang/rust/pull/84197>
+
+41. R. de C. Valle. “Tracking Issue for LLVM Control Flow Integrity (CFI) Support
for Rust #89653.” GitHub. <https://github.com/rust-lang/rust/issues/89653>.
-41. “ControlFlowIntegrity.” The Rust Unstable Book.
+42. “ControlFlowIntegrity.” The Rust Unstable Book.
[https://doc.rust-lang.org/unstable-book/compiler-flags/sanitizer.html#controlflowintegrity](../unstable-book/compiler-flags/sanitizer.html#controlflowintegrity).
-42. bbjornse. “add codegen option for using LLVM stack smash protection #84197.”
- GitHub. <https://github.com/rust-lang/rust/pull/84197>
-
-43. ivanloz. “Add support for LLVM ShadowCallStack. #98208.” GitHub.
+43. I. Lozano. “Add support for LLVM ShadowCallStack #98208.” GitHub.
<https://github.com/rust-lang/rust/pull/98208>.
44. “ShadowCallStack.” The Rust Unstable Book.
diff --git a/src/doc/rustc/src/images/image1.png b/src/doc/rustc/src/images/image1.png
index ee2d3fd4f..0da45e566 100644
--- a/src/doc/rustc/src/images/image1.png
+++ b/src/doc/rustc/src/images/image1.png
Binary files differ
diff --git a/src/doc/rustc/src/images/image2.png b/src/doc/rustc/src/images/image2.png
index 03061e1f0..a9cf23f87 100644
--- a/src/doc/rustc/src/images/image2.png
+++ b/src/doc/rustc/src/images/image2.png
Binary files differ
diff --git a/src/doc/rustc/src/images/image3.png b/src/doc/rustc/src/images/image3.png
index ef02c605e..844a2fe67 100644
--- a/src/doc/rustc/src/images/image3.png
+++ b/src/doc/rustc/src/images/image3.png
Binary files differ
diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md
index ff831a205..907e9c59f 100644
--- a/src/doc/rustc/src/platform-support.md
+++ b/src/doc/rustc/src/platform-support.md
@@ -33,9 +33,9 @@ All tier 1 targets with host tools support the full standard library.
target | notes
-------|-------
`aarch64-unknown-linux-gnu` | ARM64 Linux (kernel 4.1, glibc 2.17+) [^missing-stack-probes]
-`i686-pc-windows-gnu` | 32-bit MinGW (Windows 7+) [^windows-support]
-`i686-pc-windows-msvc` | 32-bit MSVC (Windows 7+) [^windows-support]
-`i686-unknown-linux-gnu` | 32-bit Linux (kernel 3.2+, glibc 2.17+)
+`i686-pc-windows-gnu` | 32-bit MinGW (Windows 7+) [^windows-support] [^x86_32-floats-return-ABI]
+`i686-pc-windows-msvc` | 32-bit MSVC (Windows 7+) [^windows-support] [^x86_32-floats-return-ABI]
+`i686-unknown-linux-gnu` | 32-bit Linux (kernel 3.2+, glibc 2.17+) [^x86_32-floats-return-ABI]
`x86_64-apple-darwin` | 64-bit macOS (10.12+, Sierra+)
`x86_64-pc-windows-gnu` | 64-bit MinGW (Windows 7+) [^windows-support]
`x86_64-pc-windows-msvc` | 64-bit MSVC (Windows 7+) [^windows-support]
@@ -47,7 +47,10 @@ target | notes
[^windows-support]: Only Windows 10 currently undergoes automated testing. Earlier versions of Windows rely on testing and support from the community.
+[^x86_32-floats-return-ABI]: Due to limitations of the C ABI, floating-point support on `i686` targets is non-compliant: floating-point return values are passed via an x87 register, so NaN payload bits can be lost. See [issue #114479][x86-32-float-issue].
+
[77071]: https://github.com/rust-lang/rust/issues/77071
+[x86-32-float-issue]: https://github.com/rust-lang/rust/issues/114479
## Tier 1
@@ -90,10 +93,6 @@ target | notes
`arm-unknown-linux-gnueabihf` | ARMv6 Linux, hardfloat (kernel 3.2, glibc 2.17)
`armv7-unknown-linux-gnueabihf` | ARMv7-A Linux, hardfloat (kernel 3.2, glibc 2.17)
[`loongarch64-unknown-linux-gnu`](platform-support/loongarch-linux.md) | LoongArch64 Linux, LP64D ABI (kernel 5.19, glibc 2.36)
-`mips-unknown-linux-gnu` | MIPS Linux (kernel 4.4, glibc 2.23)
-`mips64-unknown-linux-gnuabi64` | MIPS64 Linux, n64 ABI (kernel 4.4, glibc 2.23)
-`mips64el-unknown-linux-gnuabi64` | MIPS64 (LE) Linux, n64 ABI (kernel 4.4, glibc 2.23)
-`mipsel-unknown-linux-gnu` | MIPS (LE) Linux (kernel 4.4, glibc 2.23)
`powerpc-unknown-linux-gnu` | PowerPC Linux (kernel 3.2, glibc 2.17)
`powerpc64-unknown-linux-gnu` | PPC64 Linux (kernel 3.2, glibc 2.17)
`powerpc64le-unknown-linux-gnu` | PPC64LE Linux (kernel 3.10, glibc 2.17)
@@ -150,19 +149,16 @@ target | std | notes
`armv7r-none-eabi` | * | Bare ARMv7-R
`armv7r-none-eabihf` | * | Bare ARMv7-R, hardfloat
`asmjs-unknown-emscripten` | ✓ | asm.js via Emscripten
-`i586-pc-windows-msvc` | * | 32-bit Windows w/o SSE
-`i586-unknown-linux-gnu` | ✓ | 32-bit Linux w/o SSE (kernel 3.2, glibc 2.17)
-`i586-unknown-linux-musl` | ✓ | 32-bit Linux w/o SSE, MUSL
-[`i686-linux-android`](platform-support/android.md) | ✓ | 32-bit x86 Android
-`i686-unknown-freebsd` | ✓ | 32-bit FreeBSD
-`i686-unknown-linux-musl` | ✓ | 32-bit Linux with MUSL
+`i586-pc-windows-msvc` | * | 32-bit Windows w/o SSE [^x86_32-floats-x87]
+`i586-unknown-linux-gnu` | ✓ | 32-bit Linux w/o SSE (kernel 3.2, glibc 2.17) [^x86_32-floats-x87]
+`i586-unknown-linux-musl` | ✓ | 32-bit Linux w/o SSE, MUSL [^x86_32-floats-x87]
+[`i586-unknown-netbsd`](platform-support/netbsd.md) | ✓ | 32-bit x86, restricted to Pentium
+[`i686-linux-android`](platform-support/android.md) | ✓ | 32-bit x86 Android [^x86_32-floats-return-ABI]
+`i686-unknown-freebsd` | ✓ | 32-bit FreeBSD [^x86_32-floats-return-ABI]
+`i686-unknown-linux-musl` | ✓ | 32-bit Linux with MUSL [^x86_32-floats-return-ABI]
[`i686-unknown-uefi`](platform-support/unknown-uefi.md) | * | 32-bit UEFI
[`loongarch64-unknown-none`](platform-support/loongarch-none.md) | * | | LoongArch64 Bare-metal (LP64D ABI)
[`loongarch64-unknown-none-softfloat`](platform-support/loongarch-none.md) | * | | LoongArch64 Bare-metal (LP64S ABI)
-`mips-unknown-linux-musl` | ✓ | MIPS Linux with MUSL
-`mips64-unknown-linux-muslabi64` | ✓ | MIPS64 Linux, n64 ABI, MUSL
-`mips64el-unknown-linux-muslabi64` | ✓ | MIPS64 (LE) Linux, n64 ABI, MUSL
-`mipsel-unknown-linux-musl` | ✓ | MIPS (LE) Linux with MUSL
[`nvptx64-nvidia-cuda`](platform-support/nvptx64-nvidia-cuda.md) | * | --emit=asm generates PTX code that [runs on NVIDIA GPUs]
`riscv32i-unknown-none-elf` | * | Bare RISC-V (RV32I ISA)
`riscv32imac-unknown-none-elf` | * | Bare RISC-V (RV32IMAC ISA)
@@ -195,6 +191,8 @@ target | std | notes
`x86_64-unknown-redox` | ✓ | Redox OS
[`x86_64-unknown-uefi`](platform-support/unknown-uefi.md) | * | 64-bit UEFI
+[^x86_32-floats-x87]: Floating-point support on `i586` targets is non-compliant: the `x87` registers and instructions used for these targets do not provide IEEE-754-compliant behavior, in particular when it comes to rounding and NaN payload bits. See [issue #114479][x86-32-float-issue].
+
[Fortanix ABI]: https://edp.fortanix.com/
## Tier 3
@@ -220,6 +218,7 @@ target | std | host | notes
-------|:---:|:----:|-------
`aarch64-apple-ios-macabi` | ? | | Apple Catalyst on ARM64
[`aarch64-apple-tvos`](platform-support/apple-tvos.md) | ? | | ARM64 tvOS
+[`aarch64-apple-tvos-sim`](platform-support/apple-tvos.md) | ? | | ARM64 tvOS Simulator
[`aarch64-apple-watchos-sim`](platform-support/apple-watchos.md) | ✓ | | ARM64 Apple WatchOS Simulator
[`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
@@ -247,7 +246,7 @@ target | std | host | notes
`armv6-unknown-freebsd` | ✓ | ✓ | ARMv6 FreeBSD
[`armv6-unknown-netbsd-eabihf`](platform-support/netbsd.md) | ✓ | ✓ | ARMv6 NetBSD w/hard-float
[`armv6k-nintendo-3ds`](platform-support/armv6k-nintendo-3ds.md) | ? | | ARMv6K Nintendo 3DS, Horizon (Requires devkitARM toolchain)
-[`armv7-sony-vita-newlibeabihf`](platform-support/armv7-sony-vita-newlibeabihf.md) | ? | | ARMv7-A Cortex-A9 Sony PlayStation Vita (requires VITASDK toolchain)
+[`armv7-sony-vita-newlibeabihf`](platform-support/armv7-sony-vita-newlibeabihf.md) | ✓ | | ARMv7-A Cortex-A9 Sony PlayStation Vita (requires VITASDK toolchain)
[`armv7-unknown-linux-ohos`](platform-support/openharmony.md) | ✓ | | ARMv7-A OpenHarmony |
[`armv7-unknown-linux-uclibceabi`](platform-support/armv7-unknown-linux-uclibceabi.md) | ✓ | ✓ | ARMv7-A Linux with uClibc, softfloat
[`armv7-unknown-linux-uclibceabihf`](platform-support/armv7-unknown-linux-uclibceabihf.md) | ✓ | ? | ARMv7-A Linux with uClibc, hardfloat
@@ -262,23 +261,33 @@ target | std | host | notes
`avr-unknown-gnu-atmega328` | * | | AVR. Requires `-Z build-std=core`
`bpfeb-unknown-none` | * | | BPF (big endian)
`bpfel-unknown-none` | * | | BPF (little endian)
-`csky-unknown-linux-gnuabiv2` | ✓ | | C-SKY abiv2 Linux(little endian)
+`csky-unknown-linux-gnuabiv2` | ✓ | | C-SKY abiv2 Linux (little endian)
+`csky-unknown-linux-gnuabiv2hf` | ✓ | | C-SKY abiv2 Linux, hardfloat (little endian)
`hexagon-unknown-linux-musl` | ? | |
-`i386-apple-ios` | ✓ | | 32-bit x86 iOS
-[`i586-pc-nto-qnx700`](platform-support/nto-qnx.md) | * | | 32-bit x86 QNX Neutrino 7.0 RTOS |
-`i686-apple-darwin` | ✓ | ✓ | 32-bit macOS (10.12+, Sierra+)
-`i686-pc-windows-msvc` | * | | 32-bit Windows XP support
-[`i686-pc-windows-gnullvm`](platform-support/pc-windows-gnullvm.md) | ✓ | ✓ |
-`i686-unknown-haiku` | ✓ | ✓ | 32-bit Haiku
-[`i686-unknown-hurd-gnu`](platform-support/hurd.md) | ✓ | ✓ | 32-bit GNU/Hurd
-[`i686-unknown-netbsd`](platform-support/netbsd.md) | ✓ | ✓ | NetBSD/i386 with SSE2
-[`i686-unknown-openbsd`](platform-support/openbsd.md) | ✓ | ✓ | 32-bit OpenBSD
-`i686-uwp-windows-gnu` | ? | |
-`i686-uwp-windows-msvc` | ? | |
-`i686-wrs-vxworks` | ? | |
+`i386-apple-ios` | ✓ | | 32-bit x86 iOS [^x86_32-floats-return-ABI]
+[`i586-pc-nto-qnx700`](platform-support/nto-qnx.md) | * | | 32-bit x86 QNX Neutrino 7.0 RTOS [^x86_32-floats-return-ABI]
+`i686-apple-darwin` | ✓ | ✓ | 32-bit macOS (10.12+, Sierra+) [^x86_32-floats-return-ABI]
+`i686-pc-windows-msvc` | * | | 32-bit Windows XP support [^x86_32-floats-return-ABI]
+[`i686-pc-windows-gnullvm`](platform-support/pc-windows-gnullvm.md) | ✓ | ✓ | [^x86_32-floats-return-ABI]
+`i686-unknown-haiku` | ✓ | ✓ | 32-bit Haiku [^x86_32-floats-return-ABI]
+[`i686-unknown-hurd-gnu`](platform-support/hurd.md) | ✓ | ✓ | 32-bit GNU/Hurd [^x86_32-floats-return-ABI]
+[`i686-unknown-netbsd`](platform-support/netbsd.md) | ✓ | ✓ | NetBSD/i386 with SSE2 [^x86_32-floats-return-ABI]
+[`i686-unknown-openbsd`](platform-support/openbsd.md) | ✓ | ✓ | 32-bit OpenBSD [^x86_32-floats-return-ABI]
+`i686-uwp-windows-gnu` | ? | | [^x86_32-floats-return-ABI]
+`i686-uwp-windows-msvc` | ? | | [^x86_32-floats-return-ABI]
+`i686-wrs-vxworks` | ? | | [^x86_32-floats-return-ABI]
[`m68k-unknown-linux-gnu`](platform-support/m68k-unknown-linux-gnu.md) | ? | | Motorola 680x0 Linux
+`mips-unknown-linux-gnu` | ✓ | ✓ | MIPS Linux (kernel 4.4, glibc 2.23)
+`mips-unknown-linux-musl` | ✓ | | MIPS Linux with musl libc
`mips-unknown-linux-uclibc` | ✓ | | MIPS Linux with uClibc
[`mips64-openwrt-linux-musl`](platform-support/mips64-openwrt-linux-musl.md) | ? | | MIPS64 for OpenWrt Linux MUSL
+`mips64-unknown-linux-gnuabi64` | ✓ | ✓ | MIPS64 Linux, N64 ABI (kernel 4.4, glibc 2.23)
+`mips64-unknown-linux-muslabi64` | ✓ | | MIPS64 Linux, N64 ABI, musl libc
+`mips64el-unknown-linux-gnuabi64` | ✓ | ✓ | MIPS64 (little endian) Linux, N64 ABI (kernel 4.4, glibc 2.23)
+`mips64el-unknown-linux-muslabi64` | ✓ | | MIPS64 (little endian) Linux, N64 ABI, musl libc
+`mipsel-unknown-linux-gnu` | ✓ | ✓ | MIPS (little endian) Linux (kernel 4.4, glibc 2.23)
+`mipsel-unknown-linux-musl` | ✓ | | MIPS (little endian) Linux with musl libc
+[`mipsel-unknown-netbsd`](platform-support/netbsd.md) | ✓ | ✓ | 32-bit MIPS (LE), requires mips32 cpu support
`mipsel-sony-psp` | * | | MIPS (LE) Sony PlayStation Portable (PSP)
[`mipsel-sony-psx`](platform-support/mipsel-sony-psx.md) | * | | MIPS (LE) Sony PlayStation 1 (PSX)
`mipsel-unknown-linux-uclibc` | ✓ | | MIPS (LE) Linux with uClibc
@@ -301,7 +310,7 @@ target | std | host | notes
`powerpc64-wrs-vxworks` | ? | |
`powerpc64le-unknown-linux-musl` | ? | |
[`powerpc64-unknown-openbsd`](platform-support/openbsd.md) | ✓ | ✓ | OpenBSD/powerpc64
-`powerpc64-ibm-aix` | ? | | 64-bit AIX (7.2 and newer)
+[`powerpc64-ibm-aix`](platform-support/aix.md) | ? | | 64-bit AIX (7.2 and newer)
`riscv32gc-unknown-linux-gnu` | | | RISC-V Linux (kernel 5.4, glibc 2.33)
`riscv32gc-unknown-linux-musl` | | | RISC-V Linux (kernel 5.4, musl + RISCV32 support patches)
`riscv32im-unknown-none-elf` | * | | Bare RISC-V (RV32IM ISA)
diff --git a/src/doc/rustc/src/platform-support/aarch64-unknown-teeos.md b/src/doc/rustc/src/platform-support/aarch64-unknown-teeos.md
index f8cd92f92..9233a36db 100644
--- a/src/doc/rustc/src/platform-support/aarch64-unknown-teeos.md
+++ b/src/doc/rustc/src/platform-support/aarch64-unknown-teeos.md
@@ -58,7 +58,7 @@ To build a rust toolchain, create a `config.toml` with the following contents:
```toml
profile = "compiler"
-changelog-seen = 2
+change-id = 115898
[build]
sanitizers = true
diff --git a/src/doc/rustc/src/platform-support/aix.md b/src/doc/rustc/src/platform-support/aix.md
new file mode 100644
index 000000000..c3ce71a18
--- /dev/null
+++ b/src/doc/rustc/src/platform-support/aix.md
@@ -0,0 +1,26 @@
+# `powerpc64-ibm-aix`
+
+**Tier: 3**
+
+Rust for AIX operating system, currently only 64-bit PowerPC is supported.
+
+## Target maintainers
+
+- QIU Chaofan `qiucofan@cn.ibm.com`, https://github.com/ecnelises
+- Kai LUO, `lkail@cn.ibm.com`, https://github.com/bzEq
+
+## Requirements
+
+This target supports host tools, std and alloc. This target cannot be cross-compiled as for now, mainly because of the unavailability of system linker on other platforms.
+
+Binary built for this target is expected to run on Power7 or newer CPU, and AIX 7.2 or newer version.
+
+Binary format of this platform is [XCOFF](https://www.ibm.com/docs/en/aix/7.2?topic=formats-xcoff-object-file-format). Archive file format is ['AIX big format'](https://www.ibm.com/docs/en/aix/7.2?topic=formats-ar-file-format-big).
+
+## Testing
+
+This target supports running test suites natively, but it's not available to cross-compile and execute in emulator.
+
+## Interoperability with C code
+
+This target supports C code. C code compiled by XL, Open XL and Clang are compatible with Rust. Typical triple of AIX on 64-bit PowerPC of these compilers are also `powerpc64-ibm-aix`.
diff --git a/src/doc/rustc/src/platform-support/android.md b/src/doc/rustc/src/platform-support/android.md
index 4ef74295e..9ddf00e3a 100644
--- a/src/doc/rustc/src/platform-support/android.md
+++ b/src/doc/rustc/src/platform-support/android.md
@@ -45,3 +45,19 @@ The riscv64-linux-android target is supported as a Tier 3 target.
A list of all supported targets can be found
[here](../platform-support.html)
+
+## Architecture Notes
+
+### riscv64-linux-android
+
+Currently the `riscv64-linux-android` target requires the following architecture features/extensions:
+
+* `a` (atomics)
+* `d` (double-precision floating-point)
+* `c` (compressed instruction set)
+* `f` (single-precision floating-point)
+* `m` (multiplication and division)
+* `v` (vector)
+* `Zba` (address calculation instructions)
+* `Zbb` (base instructions)
+* `Zbs` (single-bit instructions)
diff --git a/src/doc/rustc/src/platform-support/apple-tvos.md b/src/doc/rustc/src/platform-support/apple-tvos.md
index d87fd1959..e7ea109df 100644
--- a/src/doc/rustc/src/platform-support/apple-tvos.md
+++ b/src/doc/rustc/src/platform-support/apple-tvos.md
@@ -52,7 +52,7 @@ The targets can be built by enabling them for a `rustc` build in `config.toml`,
```toml
[build]
build-stage = 1
-target = ["aarch64-apple-tvos", "x86_64-apple-tvos"]
+target = ["aarch64-apple-tvos", "x86_64-apple-tvos", "aarch64-apple-tvos-sim"]
```
It's possible that cargo under `-Zbuild-std` may also be used to target them.
@@ -67,6 +67,8 @@ Rust programs can be built for these targets
$ rustc --target aarch64-apple-tvos your-code.rs
...
$ rustc --target x86_64-apple-tvos your-code.rs
+...
+$ rustc --target aarch64-apple-tvos-sim your-code.rs
```
## Testing
diff --git a/src/doc/rustc/src/platform-support/armv7-sony-vita-newlibeabihf.md b/src/doc/rustc/src/platform-support/armv7-sony-vita-newlibeabihf.md
index 49eed366d..e1473bd96 100644
--- a/src/doc/rustc/src/platform-support/armv7-sony-vita-newlibeabihf.md
+++ b/src/doc/rustc/src/platform-support/armv7-sony-vita-newlibeabihf.md
@@ -2,15 +2,16 @@
**Tier: 3**
-This tier supports the ARM Cortex A9 processor running on a PlayStation Vita console. `armv7-vita-newlibeabihf` aims to have support for `std` crate using `newlib` as a bridge.
+This tier supports the ARM Cortex A9 processor running on a PlayStation Vita console.
Rust support for this target is not affiliated with Sony, and is not derived
from nor used with any official Sony SDK.
## Target maintainers
-* [@amg98](https://github.com/amg98)
* [@nikarh](https://github.com/nikarh)
+* [@pheki](https://github.com/pheki)
+* [@ZetaNumbers](https://github.com/ZetaNumbers)
## Requirements
@@ -20,18 +21,16 @@ This target is cross-compiled, and requires installing [VITASDK](https://vitasdk
`alloc`, and `panic_abort`.
`std` is partially supported, but mostly works. Some APIs are unimplemented
-and will simply return an error, such as `std::process`. An allocator is provided
-by default.
+and will simply return an error, such as `std::process`.
-In order to support some APIs, binaries must be linked against `libc` written
-for the target, using a linker for the target. These are provided by the
-VITASDK toolchain.
+This target generates binaries in the ELF format with thumb ISA by default.
+
+Binaries are linked with `arm-vita-eabi-gcc` provided by VITASDK toolchain.
-This target generates binaries in the ELF format with thumb ISA.
## Building the target
-Rust does not ship pre-compiled artifacts for this target. You can use `build-std` flag to build binaries with `std`:
+Rust does not ship pre-compiled artifacts for this target. You can use `build-std` flag to build ELF binaries with `std`:
```sh
cargo build -Z build-std=std,panic_abort --target=armv7-sony-vita-newlibeabihf --release
@@ -39,113 +38,45 @@ cargo build -Z build-std=std,panic_abort --target=armv7-sony-vita-newlibeabihf -
## Building Rust programs
-To test your developed rust programs on PlayStation Vita, first you must correctly package your elf. These steps can be preformed using tools available in VITASDK, and can be automated using a tool like `cargo-make`.
+The recommended way to build artifacts that can be installed and run on PlayStation Vita is by using the [cargo-vita](https://github.com/vita-rust/cargo-vita) tool. This tool uses `build-std` and VITASDK toolchain to build artifacts runnable on Vita.
+
+To install the tool run:
+
+```sh
+cargo install cargo-vita
+```
-First, set up environment variables for `VITASDK`, and it's binaries:
+[VITASDK](https://vitasdk.org/) toolchain must be installed, and the `VITASDK` environment variable must be set to its location, e.g.:
```sh
export VITASDK=/opt/vitasdk
-export PATH=$PATH:$VITASDK/bin
```
-Use the example below as a template for your project:
+Add the following section to your project's `Cargo.toml`:
+
```toml
-[env]
-TITLE = "Rust Hello World"
-TITLEID = "RUST00001"
-
-# At least a "sce_sys" folder should be place there for app metadata (title, icons, description...)
-# You can find sample assets for that on $VITASDK/share/gcc-arm-vita-eabi/samples/hello_world/sce_sys/
-STATIC_DIR = "static" # Folder where static assets should be placed (sce_sys folder is at $STATIC_DIR/sce_sys)
-CARGO_TARGET_DIR = { script = ["echo ${CARGO_TARGET_DIR:=target}"] }
-CARGO_OUT_DIR = "${CARGO_TARGET_DIR}/${RUST_TARGET}/release"
-
-[tasks.build]
-description = "Build the project using `cargo`."
-command = "cargo"
-args = ["build", "-Z", "build-std=std,panic_abort", "--target=armv7-sony-vita-newlibeabihf", "--release"]
-
-[tasks.strip]
-description = "Strip the produced ELF executable."
-dependencies = ["build"]
-command = "arm-vita-eabi-strip"
-args = ["-g", '${CARGO_OUT_DIR}/${CARGO_MAKE_CRATE_FS_NAME}.elf']
-
-[tasks.velf]
-description = "Build an VELF executable from the obtained ELF file."
-dependencies = ["strip"]
-command = "vita-elf-create"
-args = ['${CARGO_OUT_DIR}/${CARGO_MAKE_CRATE_NAME}.elf', '${CARGO_OUT_DIR}/${CARGO_MAKE_CRATE_NAME}.velf']
-
-[tasks.eboot-bin]
-description = "Build an `eboot.bin` file from the obtained VELF file."
-dependencies = ["velf"]
-command = "vita-make-fself"
-args = ["-s", '${CARGO_OUT_DIR}/${CARGO_MAKE_CRATE_NAME}.velf', '${CARGO_OUT_DIR}/eboot.bin']
-
-[tasks.param-sfo]
-description = "Build the `param.sfo` manifest using with given TITLE and TITLEID."
-command = "vita-mksfoex"
-args = ["-s", 'TITLE_ID=${TITLEID}', '${TITLE}', '${CARGO_OUT_DIR}/param.sfo']
-
-[tasks.manifest]
-description = "List all static resources into a manifest file."
-script = [
- 'mkdir -p "${CARGO_OUT_DIR}"',
- '''
- if [ -d "${STATIC_DIR}" ]; then
- find "${STATIC_DIR}" -type f > "${CARGO_OUT_DIR}/MANIFEST"
- else
- touch "${CARGO_OUT_DIR}/MANIFEST"
- fi
- '''
-]
-
-[tasks.vpk]
-description = "Build a VPK distribution of the project executable and resources."
-dependencies = ["eboot-bin", "param-sfo", "manifest"]
-script_runner = "@rust"
-script = [
- '''
- use std::io::BufRead;
- use std::fs::File;
-
- fn main() {
-
- let crate_name = env!("CARGO_MAKE_CRATE_NAME");
- let static_dir = env!("STATIC_DIR");
- let out_dir = std::path::PathBuf::from(env!("CARGO_OUT_DIR"));
-
- let mut cmd = ::std::process::Command::new("vita-pack-vpk");
- cmd.arg("-s").arg(out_dir.join("param.sfo"));
- cmd.arg("-b").arg(out_dir.join("eboot.bin"));
-
- // Add files from MANIFEST
- if let Ok(file) = File::open(out_dir.join("MANIFEST")) {
- let mut reader = ::std::io::BufReader::new(file);
- let mut lines = reader.lines();
- while let Some(Ok(line)) = lines.next() {
- let p1 = ::std::path::PathBuf::from(line); // path on FS
- let p2 = p1.strip_prefix(static_dir).unwrap(); // path in VPK
- cmd.arg("--add").arg(format!("{}={}", p1.display(), p2.display()));
- }
- }
-
- cmd.arg(out_dir.join(format!("{}.vpk", crate_name)))
- .output()
- .expect("command failed.");
- }
- '''
-]
+[package.metadata.vita]
+# A unique 9 character alphanumeric identifier of the app.
+title_id = "RUSTAPP01"
+# A title that will be used for the app. Optional, name will be used if not defined
+title_name = "My application"
```
-After running the above script, you should be able to get a *.vpk file in the same folder your *.elf executable resides. Now you can pick it and install it on your own PlayStation Vita using, or you can use an [Vita3K](https://vita3k.org/) emulator.
+To build a VPK with ELF in the release profile, run:
+
+```sh
+cargo vita build vpk --release
+```
+
+After building a *.vpk file it can be uploaded to a PlayStation Vita and installed, or used with a [Vita3K](https://vita3k.org/) emulator.
## Testing
-Currently there is no support to run the rustc test suite for this target.
+The default Rust test runner is supported, and tests can be compiled to an elf and packed to a *.vpk file using `cargo-vita` tool. Filtering tests is not currently supported since passing command-line arguments to the executable is not supported on Vita, so the runner will always execute all tests.
+
+The Rust test suite for `library/std` is not yet supported.
## Cross-compilation
-This target can be cross-compiled from `x86_64` on either Windows, MacOS or Linux systems. Other hosts are not supported for cross-compilation.
+This target can be cross-compiled from `x86_64` on Windows, MacOS or Linux systems. Other hosts are not supported for cross-compilation.
diff --git a/src/doc/rustc/src/platform-support/csky-unknown-linux-gnuabiv2.md b/src/doc/rustc/src/platform-support/csky-unknown-linux-gnuabiv2.md
index e73598be0..a54abcb60 100644
--- a/src/doc/rustc/src/platform-support/csky-unknown-linux-gnuabiv2.md
+++ b/src/doc/rustc/src/platform-support/csky-unknown-linux-gnuabiv2.md
@@ -4,8 +4,21 @@
This target supports [C-SKY](https://github.com/c-sky) CPUs with `abi` v2 and `glibc`.
-https://c-sky.github.io/
-https://gitlab.com/c-sky/
+target | std | host | notes
+-------|:---:|:----:|-------
+`csky-unknown-linux-gnuabiv2` | ✓ | | C-SKY abiv2 Linux (little endian)
+`csky-unknown-linux-gnuabiv2hf` | ✓ | | C-SKY abiv2 Linux, hardfloat (little endian)
+
+Reference:
+
+- [CSKY ABI Manual](https://occ-oss-prod.oss-cn-hangzhou.aliyuncs.com/resource//1695027452256/T-HEAD_800_Series_ABI_Standards_Manual.pdf)
+- [csky-linux-gnuabiv2-toolchain](https://occ-oss-prod.oss-cn-hangzhou.aliyuncs.com/resource/1356021/1619528643136/csky-linux-gnuabiv2-tools-x86_64-glibc-linux-4.9.56-20210423.tar.gz)
+- [csky-linux-gnuabiv2-qemu](https://occ-oss-prod.oss-cn-hangzhou.aliyuncs.com/resource//1689324918932/xuantie-qemu-x86_64-Ubuntu-18.04-20230714-0202.tar.gz)
+
+other links:
+
+- https://c-sky.github.io/
+- https://gitlab.com/c-sky/
## Target maintainers
@@ -13,7 +26,6 @@ https://gitlab.com/c-sky/
## Requirements
-
## Building the target
### Get a C toolchain
@@ -28,13 +40,17 @@ The target can be built by enabling it for a `rustc` build, by placing the follo
```toml
[build]
-target = ["x86_64-unknown-linux-gnu", "csky-unknown-linux-gnuabiv2"]
+target = ["x86_64-unknown-linux-gnu", "csky-unknown-linux-gnuabiv2", "csky-unknown-linux-gnuabiv2hf"]
stage = 2
[target.csky-unknown-linux-gnuabiv2]
# ADJUST THIS PATH TO POINT AT YOUR TOOLCHAIN
cc = "${TOOLCHAIN_PATH}/bin/csky-linux-gnuabiv2-gcc"
+[target.csky-unknown-linux-gnuabiv2hf]
+# ADJUST THIS PATH TO POINT AT YOUR TOOLCHAIN
+cc = "${TOOLCHAIN_PATH}/bin/csky-linux-gnuabiv2-gcc"
+
### Build
```sh
diff --git a/src/doc/rustc/src/platform-support/fuchsia.md b/src/doc/rustc/src/platform-support/fuchsia.md
index f7cce35b1..34ab3cdaf 100644
--- a/src/doc/rustc/src/platform-support/fuchsia.md
+++ b/src/doc/rustc/src/platform-support/fuchsia.md
@@ -692,10 +692,12 @@ Fuchsia's test runner interacts with the Fuchsia emulator and is located at
test environment with:
```sh
-src/ci/docker/scripts/fuchsia-test-runner.py start \
+( \
+ src/ci/docker/scripts/fuchsia-test-runner.py start \
--rust-build ${RUST_SRC_PATH}/build \
--sdk ${SDK_PATH} \
--target {x86_64-unknown-fuchsia|aarch64-unknown-fuchsia} \
+)
```
Where `${RUST_SRC_PATH}/build` is the `build-dir` set in `config.toml` and
diff --git a/src/doc/rustc/src/platform-support/mips-release-6.md b/src/doc/rustc/src/platform-support/mips-release-6.md
index 3f1912fc6..9203a31e9 100644
--- a/src/doc/rustc/src/platform-support/mips-release-6.md
+++ b/src/doc/rustc/src/platform-support/mips-release-6.md
@@ -67,7 +67,7 @@ The following procedure outlines the build process for the MIPS64 R6 target with
### Prerequisite: Disable debuginfo
-A LLVM bug makes rustc crash if debug or debug info generation is enabled. You need to edit `config.toml` to disable this:
+An LLVM bug makes rustc crash if debug or debug info generation is enabled. You need to edit `config.toml` to disable this:
```toml
[rust]
diff --git a/src/doc/rustc/src/platform-support/nto-qnx.md b/src/doc/rustc/src/platform-support/nto-qnx.md
index b376c4a84..9f0662783 100644
--- a/src/doc/rustc/src/platform-support/nto-qnx.md
+++ b/src/doc/rustc/src/platform-support/nto-qnx.md
@@ -98,7 +98,7 @@ Example content:
```toml
profile = "compiler"
-changelog-seen = 2
+change-id = 115898
```
2. Compile the Rust toolchain for an `x86_64-unknown-linux-gnu` host (for both `aarch64` and `x86_64` targets)
diff --git a/src/doc/rustc/src/platform-support/openharmony.md b/src/doc/rustc/src/platform-support/openharmony.md
index 89539f388..05fd407ed 100644
--- a/src/doc/rustc/src/platform-support/openharmony.md
+++ b/src/doc/rustc/src/platform-support/openharmony.md
@@ -101,7 +101,7 @@ To build a rust toolchain, create a `config.toml` with the following contents:
```toml
profile = "compiler"
-changelog-seen = 2
+change-id = 115898
[build]
sanitizers = true
diff --git a/src/doc/rustc/src/platform-support/unknown-uefi.md b/src/doc/rustc/src/platform-support/unknown-uefi.md
index 68cd7fae3..1230ea22b 100644
--- a/src/doc/rustc/src/platform-support/unknown-uefi.md
+++ b/src/doc/rustc/src/platform-support/unknown-uefi.md
@@ -265,9 +265,14 @@ cargo build --target x86_64-unknown-uefi -Zbuild-std=std,panic_abort
#### os_str
- While the strings in UEFI should be valid UCS-2, in practice, many implementations just do not care and use UTF-16 strings.
- Thus, the current implementation supports full UTF-16 strings.
+#### stdio
+- Uses `Simple Text Input Protocol` and `Simple Text Output Protocol`.
+- Note: UEFI uses CRLF for new line. This means Enter key is registered as CR instead of LF.
+#### args
+- Uses `EFI_LOADED_IMAGE_PROTOCOL->LoadOptions`
## Example: Hello World With std
-The following code features a valid UEFI application, including stdio and `alloc` (`OsString` and `Vec`):
+The following code features a valid UEFI application, including `stdio` and `alloc` (`OsString` and `Vec`):
This example can be compiled as binary crate via `cargo` using the toolchain
compiled from the above source (named custom):
@@ -286,6 +291,9 @@ use std::{
};
pub fn main() {
+ println!("Starting Rust Application...");
+
+ // Use System Table Directly
let st = env::system_table().as_ptr() as *mut efi::SystemTable;
let mut s: Vec<u16> = OsString::from("Hello World!\n").encode_wide().collect();
s.push(0);
diff --git a/src/doc/rustc/src/profile-guided-optimization.md b/src/doc/rustc/src/profile-guided-optimization.md
index d9cf7ce30..38b655b75 100644
--- a/src/doc/rustc/src/profile-guided-optimization.md
+++ b/src/doc/rustc/src/profile-guided-optimization.md
@@ -145,3 +145,26 @@ in Clang's documentation is therefore an interesting read for anyone who wants
to use PGO with Rust.
[clang-pgo]: https://clang.llvm.org/docs/UsersManual.html#profile-guided-optimization
+
+## Community Maintained Tools
+
+As an alternative to directly using the compiler for Profile-Guided Optimization,
+you may choose to go with `cargo-pgo`, which has an intuitive command-line API
+and saves you the trouble of doing all the manual work. You can read more about
+it in their repository accessible from this link: https://github.com/Kobzol/cargo-pgo
+
+For the sake of completeness, here are the corresponding steps using `cargo-pgo`:
+
+```bash
+# Install if you haven't already
+cargo install cargo-pgo
+
+cargo pgo build
+cargo pgo optimize
+```
+
+These steps will do the following just as before:
+
+1. Build an instrumented binary from the source code.
+2. Run the instrumented binary to gather PGO profiles.
+3. Use the gathered PGO profiles from the last step to build an optimized binary.
diff --git a/src/doc/rustdoc/src/advanced-features.md b/src/doc/rustdoc/src/advanced-features.md
index dbf0baec0..1733c8fc9 100644
--- a/src/doc/rustdoc/src/advanced-features.md
+++ b/src/doc/rustdoc/src/advanced-features.md
@@ -110,3 +110,23 @@ https://doc.rust-lang.org/stable/std/?search=%s&go_to_first=true
This URL adds the `go_to_first=true` query parameter which can be appended to any `rustdoc` search URL
to automatically go to the first result.
+
+## `#[repr(transparent)]`: Documenting the transparent representation
+
+You can read more about `#[repr(transparent)]` itself in the [Rust Reference][repr-trans-ref] and
+in the [Rustonomicon][repr-trans-nomicon].
+
+Since this representation is only considered part of the public ABI if the single field with non-trivial
+size or alignment is public and if the documentation does not state otherwise, Rustdoc helpfully displays
+the attribute if and only if the non-1-ZST field is public or at least one field is public in case all
+fields are 1-ZST fields. The term *1-ZST* refers to types that are one-aligned and zero-sized.
+
+It would seem that one can manually hide the attribute with `#[cfg_attr(not(doc), repr(transparent))]`
+if one wishes to declare the representation as private even if the non-1-ZST field is public.
+However, due to [current limitations][cross-crate-cfg-doc], this method is not always guaranteed to work.
+Therefore, if you would like to do so, you should always write it down in prose independently of whether
+you use `cfg_attr` or not.
+
+[repr-trans-ref]: https://doc.rust-lang.org/reference/type-layout.html#the-transparent-representation
+[repr-trans-nomicon]: https://doc.rust-lang.org/nomicon/other-reprs.html#reprtransparent
+[cross-crate-cfg-doc]: https://github.com/rust-lang/rust/issues/114952
diff --git a/src/doc/rustdoc/src/unstable-features.md b/src/doc/rustdoc/src/unstable-features.md
index 7473b0992..41602dec4 100644
--- a/src/doc/rustdoc/src/unstable-features.md
+++ b/src/doc/rustdoc/src/unstable-features.md
@@ -207,6 +207,21 @@ To do so, the `#[doc(keyword = "...")]` attribute is used. Example:
mod empty_mod {}
```
+### Use the Rust logo as the crate logo
+
+This is for official Rust project use only.
+
+Internal Rustdoc pages like settings.html and scrape-examples-help.html show the Rust logo.
+This logo is tracked as a static resource. The attribute `#![doc(rust_logo)]` makes this same
+built-in resource act as the main logo.
+
+```rust
+#![feature(rustdoc_internals)]
+#![allow(internal_features)]
+#![doc(rust_logo)]
+//! This crate has the Rust(tm) branding on it.
+```
+
## Effects of other nightly features
These nightly-only features are not primarily related to Rustdoc,
@@ -613,10 +628,10 @@ Using this flag looks like this:
```bash
$ rustdoc src/lib.rs -Z unstable-options \
- --check-cfg='names()' --check-cfg='values(feature, "foo", "bar")'
+ --check-cfg='cfg(feature, values("foo", "bar"))'
```
-The example above check every well known names (`target_os`, `doc`, `test`, ... via `names()`)
+The example above check every well known names and values (`target_os`, `doc`, `test`, ...)
and check the values of `feature`: `foo` and `bar`.
### `--generate-link-to-definition`: Generate links on types in source code
diff --git a/src/doc/rustdoc/src/write-documentation/what-to-include.md b/src/doc/rustdoc/src/write-documentation/what-to-include.md
index 16457ed0f..75d3b7dae 100644
--- a/src/doc/rustdoc/src/write-documentation/what-to-include.md
+++ b/src/doc/rustdoc/src/write-documentation/what-to-include.md
@@ -73,7 +73,7 @@ and your test suite, this example needs some additional code:
``````text
/// Example
/// ```rust
-/// # main() -> Result<(), std::num::ParseIntError> {
+/// # fn main() -> Result<(), std::num::ParseIntError> {
/// let fortytwo = "42".parse::<u32>()?;
/// println!("{} + 10 = {}", fortytwo, fortytwo+10);
/// # Ok(())
@@ -117,7 +117,7 @@ rustdoc --theme awesome.css src/lib.rs
Here is an example of a new theme, [Ayu].
-[Ayu]: https://github.com/rust-lang/rust/blob/master/src/librustdoc/html/static/css/themes/ayu.css
+[Ayu]: https://github.com/rust-lang/rust/blob/master/src/librustdoc/html/static/css/rustdoc.css#L2384-L2574
[API Guidelines]: https://rust-lang.github.io/api-guidelines/documentation.html#rustdoc-does-not-show-unhelpful-implementation-details-c-hidden
[Documentation tests]: documentation-tests.md
[on this blog]: https://blog.guillaume-gomez.fr/articles/2016-09-16+Generating+doc+with+rustdoc+and+a+custom+theme
diff --git a/src/doc/unstable-book/src/compiler-flags/check-cfg.md b/src/doc/unstable-book/src/compiler-flags/check-cfg.md
index 10f0fbc50..0e15c7907 100644
--- a/src/doc/unstable-book/src/compiler-flags/check-cfg.md
+++ b/src/doc/unstable-book/src/compiler-flags/check-cfg.md
@@ -10,97 +10,80 @@ This feature allows you to enable complete or partial checking of configuration.
check them. The `--check-cfg` option takes a value, called the _check cfg specification_. The
check cfg specification is parsed using the Rust metadata syntax, just as the `--cfg` option is.
-`--check-cfg` option can take one of two forms:
+`--check-cfg` option take one form:
-1. `--check-cfg names(...)` enables checking condition names.
-2. `--check-cfg values(...)` enables checking the values within list-valued conditions.
-
-These two options are independent. `names` checks only the namespace of condition names
-while `values` checks only the namespace of the values of list-valued conditions.
+1. `--check-cfg cfg(...)` enables checking the values within list-valued conditions.
NOTE: No implicit expectation is added when using `--cfg` for both forms. Users are expected to
-pass all expected names and values using `names(...)` and `values(...)`.
+pass all expected names and values using `cfg(...)`.
-## The `names(...)` form
+## The `cfg(...)` form
-The `names(...)` form enables checking the names. This form uses a named list:
+The `cfg(...)` form enables checking the values within list-valued conditions. It has this
+basic form:
```bash
-rustc --check-cfg 'names(name1, name2, ... nameN)'
+rustc --check-cfg 'cfg(name1, ..., nameN, values("value1", "value2", ... "valueN"))'
```
-where each `name` is a bare identifier (has no quotes). The order of the names is not significant.
+where `name` is a bare identifier (has no quotes) and each `"value"` term is a quoted literal
+string. `name` specifies the name of the condition, such as `feature` or `my_cfg`.
-If `--check-cfg names(...)` is specified at least once, then `rustc` will check all references to
-condition names. `rustc` will check every `#[cfg]` attribute, `#[cfg_attr]` attribute, `cfg` clause
-inside `#[link]` attribute and `cfg!(...)` call against the provided list of expected condition
-names. If a name is not present in this list, then `rustc` will report an `unexpected_cfgs` lint
-diagnostic. The default diagnostic level for this lint is `Warn`.
+When the `cfg(...)` option is specified, `rustc` will check every `#[cfg(name = "value")]`
+attribute, `#[cfg_attr(name = "value")]` attribute, `#[link(name = "a", cfg(name = "value"))]`
+and `cfg!(name = "value")` call. It will check that the `"value"` specified is present in the
+list of expected values. If `"value"` is not in it, then `rustc` will report an `unexpected_cfgs`
+lint diagnostic. The default diagnostic level for this lint is `Warn`.
-If `--check-cfg names(...)` is not specified, then `rustc` will not check references to condition
-names.
+To enable checking of values, but to provide an empty set of expected values, use these forms:
-`--check-cfg names(...)` may be specified more than once. The result is that the list of valid
-condition names is merged across all options. It is legal for a condition name to be specified
-more than once; redundantly specifying a condition name has no effect.
+```bash
+rustc --check-cfg 'cfg(name1, ..., nameN)'
+rustc --check-cfg 'cfg(name1, ..., nameN, values())'
+```
-To enable checking condition names with an empty set of valid condition names, use the following
-form. The parentheses are required.
+To enable checking of name but not values (i.e. unknown expected values), use this form:
```bash
-rustc --check-cfg 'names()'
+rustc --check-cfg 'cfg(name1, ..., nameN, values(any()))'
```
-Note that `--check-cfg 'names()'` is _not_ equivalent to omitting the option entirely.
-The first form enables checking condition names, while specifying that there are no valid
-condition names (outside of the set of well-known names defined by `rustc`). Omitting the
-`--check-cfg 'names(...)'` option does not enable checking condition names.
-
-## The `values(...)` form
+The `--check-cfg cfg(...)` option can be repeated, both for the same condition name and for
+different names. If it is repeated for the same condition name, then the sets of values for that
+condition are merged together (presedence is given to `any()`).
-The `values(...)` form enables checking the values within list-valued conditions. It has this
-form:
+## Well known names and values
-```bash
-rustc --check-cfg `values(name, "value1", "value2", ... "valueN")'
-```
+`rustc` has a internal list of well known names and their corresponding values.
+Those well known names and values follows the same stability as what they refer to.
-where `name` is a bare identifier (has no quotes) and each `"value"` term is a quoted literal
-string. `name` specifies the name of the condition, such as `feature` or `target_os`.
+Well known values checking is always enabled as long as a `--check-cfg` argument is present.
-When the `values(...)` option is specified, `rustc` will check every `#[cfg(name = "value")]`
-attribute, `#[cfg_attr(name = "value")]` attribute, `#[link(name = "a", cfg(name = "value"))]`
-and `cfg!(name = "value")` call. It will check that the `"value"` specified is present in the
-list of expected values. If `"value"` is not in it, then `rustc` will report an `unexpected_cfgs`
-lint diagnostic. The default diagnostic level for this lint is `Warn`.
+Well known names checking is always enable as long as a `--check-cfg` argument is present
+**unless** any `cfg(any())` argument is passed.
-To enable checking of values, but to provide an empty set of valid values, use this form:
+To disable checking of well known names, use this form:
```bash
-rustc --check-cfg `values(name)`
+rustc --check-cfg 'cfg(any())'
```
-The `--check-cfg values(...)` option can be repeated, both for the same condition name and for
-different names. If it is repeated for the same condition name, then the sets of values for that
-condition are merged together.
-
-If `values()` is specified, then `rustc` will enable the checking of well-known values defined
-by itself. Note that it's necessary to specify the `values()` form to enable the checking of
-well known values, specifying the other forms doesn't implicitly enable it.
+NOTE: If one want to enable values and names checking without having any cfg to declare, one
+can use an empty `cfg()` argument.
## Examples
Consider this command line:
```bash
-rustc --check-cfg 'names(feature)' \
- --check-cfg 'values(feature, "lion", "zebra")' \
+rustc --check-cfg 'cfg(feature, values("lion", "zebra"))' \
--cfg 'feature="lion"' -Z unstable-options \
example.rs
```
This command line indicates that this crate has two features: `lion` and `zebra`. The `lion`
-feature is enabled, while the `zebra` feature is disabled. Consider compiling this code:
+feature is enabled, while the `zebra` feature is disabled. Exhaustive checking of names and
+values are enabled by default. Consider compiling this code:
```rust
// This is expected, and tame_lion() will be compiled
@@ -119,35 +102,36 @@ fn poke_platypus() {}
// and will cause a compiler warning (by default).
#[cfg(feechure = "lion")]
fn tame_lion() {}
-```
-> Note: The `--check-cfg names(feature)` option is necessary only to enable checking the condition
-> name, as in the last example. `feature` is a well-known (always-expected) condition name, and so
-> it is not necessary to specify it in a `--check-cfg 'names(...)'` option. That option can be
-> shortened to > `--check-cfg names()` in order to enable checking well-known condition names.
+// This is UNEXPECTED, because 'windows' is a well known condition name,
+// and because 'windows' doens't take any values,
+// and will cause a compiler warning (by default).
+#[cfg(windows = "unix")]
+fn tame_windows() {}
+```
### Example: Checking condition names, but not values
```bash
# This turns on checking for condition names, but not values, such as 'feature' values.
-rustc --check-cfg 'names(is_embedded, has_feathers)' \
+rustc --check-cfg 'cfg(is_embedded, has_feathers, values(any()))' \
--cfg has_feathers -Z unstable-options
```
```rust
-#[cfg(is_embedded)] // This is expected as "is_embedded" was provided in names()
-fn do_embedded() {}
+#[cfg(is_embedded)] // This is expected as "is_embedded" was provided in cfg()
+fn do_embedded() {} // and because names exhaustiveness was not disabled
-#[cfg(has_feathers)] // This is expected as "has_feathers" was provided in names()
-fn do_features() {}
+#[cfg(has_feathers)] // This is expected as "has_feathers" was provided in cfg()
+fn do_features() {} // and because names exhaustiveness was not disabled
-#[cfg(has_feathers = "zapping")] // This is expected as "has_feathers" was provided in names()
+#[cfg(has_feathers = "zapping")] // This is expected as "has_feathers" was provided in cfg()
// and because no value checking was enable for "has_feathers"
// no warning is emitted for the value "zapping"
fn do_zapping() {}
#[cfg(has_mumble_frotz)] // This is UNEXPECTED because names checking is enable and
- // "has_mumble_frotz" was not provided in names()
+ // "has_mumble_frotz" was not provided in cfg()
fn do_mumble_frotz() {}
```
@@ -155,25 +139,25 @@ fn do_mumble_frotz() {}
```bash
# This turns on checking for feature values, but not for condition names.
-rustc --check-cfg 'values(feature, "zapping", "lasers")' \
+rustc --check-cfg 'cfg(feature, values("zapping", "lasers"))' \
+ --check-cfg 'cfg(any())' \
--cfg 'feature="zapping"' -Z unstable-options
```
```rust
-#[cfg(is_embedded)] // This is doesn't raise a warning, because names checking was not
- // enable (ie not names())
+#[cfg(is_embedded)] // This is doesn't raise a warning, because names checking was
+ // disabled by 'cfg(any())'
fn do_embedded() {}
-#[cfg(has_feathers)] // Same as above, --check-cfg names(...) was never used so no name
+#[cfg(has_feathers)] // Same as above, 'cfg(any())' was provided so no name
// checking is performed
fn do_features() {}
-
-#[cfg(feature = "lasers")] // This is expected, "lasers" is in the values(feature) list
+#[cfg(feature = "lasers")] // This is expected, "lasers" is in the cfg(feature) list
fn shoot_lasers() {}
#[cfg(feature = "monkeys")] // This is UNEXPECTED, because "monkeys" is not in the
- // --check-cfg values(feature) list
+ // cfg(feature) list
fn write_shakespeare() {}
```
@@ -181,26 +165,92 @@ fn write_shakespeare() {}
```bash
# This turns on checking for feature values and for condition names.
-rustc --check-cfg 'names(is_embedded, has_feathers)' \
- --check-cfg 'values(feature, "zapping", "lasers")' \
+rustc --check-cfg 'cfg(is_embedded, has_feathers)' \
+ --check-cfg 'cfg(feature, values("zapping", "lasers"))' \
--cfg has_feathers --cfg 'feature="zapping"' -Z unstable-options
```
```rust
-#[cfg(is_embedded)] // This is expected because "is_embedded" was provided in names()
-fn do_embedded() {}
+#[cfg(is_embedded)] // This is expected because "is_embedded" was provided in cfg()
+fn do_embedded() {} // and doesn't take any value
-#[cfg(has_feathers)] // This is expected because "has_feathers" was provided in names()
-fn do_features() {}
+#[cfg(has_feathers)] // This is expected because "has_feathers" was provided in cfg()
+fn do_features() {} // and deosn't take any value
-#[cfg(has_mumble_frotz)] // This is UNEXPECTED, because has_mumble_frotz is not in the
- // --check-cfg names(...) list
+#[cfg(has_mumble_frotz)] // This is UNEXPECTED, because "has_mumble_frotz" was never provided
fn do_mumble_frotz() {}
-#[cfg(feature = "lasers")] // This is expected, "lasers" is in the values(feature) list
+#[cfg(feature = "lasers")] // This is expected, "lasers" is in the cfg(feature) list
fn shoot_lasers() {}
#[cfg(feature = "monkeys")] // This is UNEXPECTED, because "monkeys" is not in
- // the values(feature) list
+ // the cfg(feature) list
fn write_shakespeare() {}
```
+
+## The deprecated `names(...)` form
+
+The `names(...)` form enables checking the names. This form uses a named list:
+
+```bash
+rustc --check-cfg 'names(name1, name2, ... nameN)'
+```
+
+where each `name` is a bare identifier (has no quotes). The order of the names is not significant.
+
+If `--check-cfg names(...)` is specified at least once, then `rustc` will check all references to
+condition names. `rustc` will check every `#[cfg]` attribute, `#[cfg_attr]` attribute, `cfg` clause
+inside `#[link]` attribute and `cfg!(...)` call against the provided list of expected condition
+names. If a name is not present in this list, then `rustc` will report an `unexpected_cfgs` lint
+diagnostic. The default diagnostic level for this lint is `Warn`.
+
+If `--check-cfg names(...)` is not specified, then `rustc` will not check references to condition
+names.
+
+`--check-cfg names(...)` may be specified more than once. The result is that the list of valid
+condition names is merged across all options. It is legal for a condition name to be specified
+more than once; redundantly specifying a condition name has no effect.
+
+To enable checking condition names with an empty set of valid condition names, use the following
+form. The parentheses are required.
+
+```bash
+rustc --check-cfg 'names()'
+```
+
+Note that `--check-cfg 'names()'` is _not_ equivalent to omitting the option entirely.
+The first form enables checking condition names, while specifying that there are no valid
+condition names (outside of the set of well-known names defined by `rustc`). Omitting the
+`--check-cfg 'names(...)'` option does not enable checking condition names.
+
+## The deprecated `values(...)` form
+
+The `values(...)` form enables checking the values within list-valued conditions. It has this
+form:
+
+```bash
+rustc --check-cfg `values(name, "value1", "value2", ... "valueN")'
+```
+
+where `name` is a bare identifier (has no quotes) and each `"value"` term is a quoted literal
+string. `name` specifies the name of the condition, such as `feature` or `target_os`.
+
+When the `values(...)` option is specified, `rustc` will check every `#[cfg(name = "value")]`
+attribute, `#[cfg_attr(name = "value")]` attribute, `#[link(name = "a", cfg(name = "value"))]`
+and `cfg!(name = "value")` call. It will check that the `"value"` specified is present in the
+list of expected values. If `"value"` is not in it, then `rustc` will report an `unexpected_cfgs`
+lint diagnostic. The default diagnostic level for this lint is `Warn`.
+
+To enable checking of values, but to provide an empty set of valid values, use this form:
+
+```bash
+rustc --check-cfg `values(name)`
+```
+
+The `--check-cfg values(...)` option can be repeated, both for the same condition name and for
+different names. If it is repeated for the same condition name, then the sets of values for that
+condition are merged together.
+
+If `values()` is specified, then `rustc` will enable the checking of well-known values defined
+by itself. Note that it's necessary to specify the `values()` form to enable the checking of
+well known values, specifying the other forms doesn't implicitly enable it.
diff --git a/src/doc/unstable-book/src/compiler-flags/no-jump-tables.md b/src/doc/unstable-book/src/compiler-flags/no-jump-tables.md
new file mode 100644
index 000000000..f096c20f4
--- /dev/null
+++ b/src/doc/unstable-book/src/compiler-flags/no-jump-tables.md
@@ -0,0 +1,19 @@
+# `no-jump-tables`
+
+The tracking issue for this feature is [#116592](https://github.com/rust-lang/rust/issues/116592)
+
+---
+
+This option enables the `-fno-jump-tables` flag for LLVM, which makes the
+codegen backend avoid generating jump tables when lowering switches.
+
+This option adds the LLVM `no-jump-tables=true` attribute to every function.
+
+The option can be used to help provide protection against
+jump-oriented-programming (JOP) attacks, such as with the linux kernel's [IBT].
+
+```sh
+RUSTFLAGS="-Zno-jump-tables" cargo +nightly build -Z build-std
+```
+
+[IBT]: https://www.phoronix.com/news/Linux-IBT-By-Default-Tip
diff --git a/src/doc/unstable-book/src/compiler-flags/remap-path-scope.md b/src/doc/unstable-book/src/compiler-flags/remap-path-scope.md
new file mode 100644
index 000000000..13349ff6b
--- /dev/null
+++ b/src/doc/unstable-book/src/compiler-flags/remap-path-scope.md
@@ -0,0 +1,24 @@
+# `remap-path-scope`
+
+The tracking issue for this feature is: [#111540](https://github.com/rust-lang/rust/issues/111540).
+
+------------------------
+
+When the `--remap-path-prefix` option is passed to rustc, source path prefixes in all output will be affected by default.
+The `--remap-path-scope` argument can be used in conjunction with `--remap-path-prefix` to determine paths in which output context should be affected.
+This flag accepts a comma-separated list of values and may be specified multiple times, in which case the scopes are aggregated together. The valid scopes are:
+
+- `macro` - apply remappings to the expansion of `std::file!()` macro. This is where paths in embedded panic messages come from
+- `diagnostics` - apply remappings to printed compiler diagnostics
+- `unsplit-debuginfo` - apply remappings to debug information only when they are written to compiled executables or libraries, but not when they are in split debuginfo files
+- `split-debuginfo` - apply remappings to debug information only when they are written to split debug information files, but not in compiled executables or libraries
+- `split-debuginfo-path` - apply remappings to the paths pointing to split debug information files. Does nothing when these files are not generated.
+- `object` - an alias for `macro,unsplit-debuginfo,split-debuginfo-path`. This ensures all paths in compiled executables or libraries are remapped, but not elsewhere.
+- `all` and `true` - an alias for all of the above, also equivalent to supplying only `--remap-path-prefix` without `--remap-path-scope`.
+
+## Example
+```sh
+# This would produce an absolute path to main.rs in build outputs of
+# "./main.rs".
+rustc --remap-path-prefix=$(PWD)=/remapped -Zremap-path-prefix=object main.rs
+```
diff --git a/src/doc/unstable-book/src/compiler-flags/sanitizer.md b/src/doc/unstable-book/src/compiler-flags/sanitizer.md
index 49389b28c..502853f39 100644
--- a/src/doc/unstable-book/src/compiler-flags/sanitizer.md
+++ b/src/doc/unstable-book/src/compiler-flags/sanitizer.md
@@ -197,22 +197,26 @@ Shadow byte legend (one shadow byte represents 8 application bytes):
# ControlFlowIntegrity
-The LLVM Control Flow Integrity (CFI) support in the Rust compiler provides
-forward-edge control flow protection for both Rust-compiled code only and for C
-or C++ and Rust -compiled code mixed-language binaries, also known as “mixed
-binaries” (i.e., for when C or C++ and Rust -compiled code share the same
-virtual address space), by aggregating function pointers in groups identified by
-their return and parameter types.
-
-LLVM CFI can be enabled with `-Zsanitizer=cfi` and requires LTO (i.e., `-Clto`).
-Cross-language LLVM CFI can be enabled with `-Zsanitizer=cfi`, and requires the
-`-Zsanitizer-cfi-normalize-integers` option to be used with Clang
-`-fsanitize-cfi-icall-normalize-integers` for normalizing integer types, and
-proper (i.e., non-rustc) LTO (i.e., `-Clinker-plugin-lto`).
+The LLVM CFI support in the Rust compiler provides forward-edge control flow
+protection for both Rust-compiled code only and for C or C++ and Rust -compiled
+code mixed-language binaries, also known as “mixed binaries” (i.e., for when C
+or C++ and Rust -compiled code share the same virtual address space), by
+aggregating function pointers in groups identified by their return and parameter
+types.
+
+LLVM CFI can be enabled with `-Zsanitizer=cfi` and requires LTO (i.e.,
+`-Clinker-plugin-lto` or `-Clto`). Cross-language LLVM CFI can be enabled with
+`-Zsanitizer=cfi`, and requires the `-Zsanitizer-cfi-normalize-integers` option
+to be used with Clang `-fsanitize-cfi-icall-experimental-normalize-integers`
+option for cross-language LLVM CFI support, and proper (i.e., non-rustc) LTO
+(i.e., `-Clinker-plugin-lto`).
+
+It is recommended to rebuild the standard library with CFI enabled by using the
+Cargo build-std feature (i.e., `-Zbuild-std`) when enabling CFI.
See the [Clang ControlFlowIntegrity documentation][clang-cfi] for more details.
-## Example
+## Example 1: Redirecting control flow using an indirect branch/call to an invalid destination
```rust,ignore (making doc tests pass cross-platform is hard)
#![feature(naked_functions)]
@@ -239,7 +243,7 @@ pub extern "C" fn add_two(x: i32) {
nop
nop
nop
- lea eax, [edi+2]
+ lea eax, [rdi+2]
ret
",
options(noreturn)
@@ -258,8 +262,9 @@ fn main() {
println!("With CFI enabled, you should not see the next answer");
let f: fn(i32) -> i32 = unsafe {
- // Offsets 0-8 make it land in the landing pad/nop block, and offsets 1-8 are
- // invalid branch/call destinations (i.e., within the body of the function).
+ // Offset 0 is a valid branch/call destination (i.e., the function entry
+ // point), but offsets 1-8 within the landing pad/nop block are invalid
+ // branch/call destinations (i.e., within the body of the function).
mem::transmute::<*const u8, fn(i32) -> i32>((add_two as *const u8).offset(5))
};
let next_answer = do_twice(f, 5);
@@ -267,38 +272,40 @@ fn main() {
println!("The next answer is: {}", next_answer);
}
```
-Fig. 1. Modified example from the [Advanced Functions and
-Closures][rust-book-ch19-05] chapter of the [The Rust Programming
-Language][rust-book] book.
+Fig. 1. Redirecting control flow using an indirect branch/call to an invalid
+destination (i.e., within the body of the function).
```shell
$ cargo run --release
Compiling rust-cfi-1 v0.1.0 (/home/rcvalle/rust-cfi-1)
- Finished release [optimized] target(s) in 0.76s
+ Finished release [optimized] target(s) in 0.42s
Running `target/release/rust-cfi-1`
The answer is: 12
With CFI enabled, you should not see the next answer
The next answer is: 14
$
```
-Fig. 2. Build and execution of the modified example with LLVM CFI disabled.
+Fig. 2. Build and execution of Fig. 1 with LLVM CFI disabled.
```shell
-$ RUSTFLAGS="-Zsanitizer=cfi -Cembed-bitcode=yes -Clto" cargo run --release
+$ RUSTFLAGS="-Clinker-plugin-lto -Clinker=clang -Clink-arg=-fuse-ld=lld -Zsanitizer=cfi" cargo run -Zbuild-std -Zbuild-std-features --release --target x86_64-unknown-linux-gnu
+ ...
Compiling rust-cfi-1 v0.1.0 (/home/rcvalle/rust-cfi-1)
- Finished release [optimized] target(s) in 3.39s
- Running `target/release/rust-cfi-1`
+ Finished release [optimized] target(s) in 1m 08s
+ Running `target/x86_64-unknown-linux-gnu/release/rust-cfi-1`
The answer is: 12
With CFI enabled, you should not see the next answer
Illegal instruction
$
```
-Fig. 3. Build and execution of the modified example with LLVM CFI enabled.
+Fig. 3. Build and execution of Fig. 1 with LLVM CFI enabled.
When LLVM CFI is enabled, if there are any attempts to change/hijack control
flow using an indirect branch/call to an invalid destination, the execution is
terminated (see Fig. 3).
+## Example 2: Redirecting control flow using an indirect branch/call to a function with a different number of parameters
+
```rust
use std::mem;
@@ -327,39 +334,42 @@ fn main() {
println!("The next answer is: {}", next_answer);
}
```
-Fig. 4. Another modified example from the [Advanced Functions and
-Closures][rust-book-ch19-05] chapter of the [The Rust Programming
-Language][rust-book] book.
+Fig. 4. Redirecting control flow using an indirect branch/call to a function
+with a different number of parameters than arguments intended/passed in the
+call/branch site.
```shell
$ cargo run --release
Compiling rust-cfi-2 v0.1.0 (/home/rcvalle/rust-cfi-2)
- Finished release [optimized] target(s) in 0.76s
+ Finished release [optimized] target(s) in 0.43s
Running `target/release/rust-cfi-2`
The answer is: 12
With CFI enabled, you should not see the next answer
The next answer is: 14
$
```
-Fig. 5. Build and execution of the modified example with LLVM CFI disabled.
+Fig. 5. Build and execution of Fig. 4 with LLVM CFI disabled.
```shell
-$ RUSTFLAGS="-Cembed-bitcode=yes -Clto -Zsanitizer=cfi" cargo run --release
+$ RUSTFLAGS="-Clinker-plugin-lto -Clinker=clang -Clink-arg=-fuse-ld=lld -Zsanitizer=cfi" cargo run -Zbuild-std -Zbuild-std-features --release --target x86_64-unknown-linux-gnu
+ ...
Compiling rust-cfi-2 v0.1.0 (/home/rcvalle/rust-cfi-2)
- Finished release [optimized] target(s) in 3.38s
- Running `target/release/rust-cfi-2`
+ Finished release [optimized] target(s) in 1m 08s
+ Running `target/x86_64-unknown-linux-gnu/release/rust-cfi-2`
The answer is: 12
With CFI enabled, you should not see the next answer
Illegal instruction
$
```
-Fig. 6. Build and execution of the modified example with LLVM CFI enabled.
+Fig. 6. Build and execution of Fig. 4 with LLVM CFI enabled.
When LLVM CFI is enabled, if there are any attempts to change/hijack control
flow using an indirect branch/call to a function with different number of
parameters than arguments intended/passed in the call/branch site, the
execution is also terminated (see Fig. 6).
+## Example 3: Redirecting control flow using an indirect branch/call to a function with different return and parameter types
+
```rust
use std::mem;
@@ -388,42 +398,46 @@ fn main() {
println!("The next answer is: {}", next_answer);
}
```
-Fig. 7. Another modified example from the [Advanced Functions and
-Closures][rust-book-ch19-05] chapter of the [The Rust Programming
-Language][rust-book] book.
+Fig. 7. Redirecting control flow using an indirect branch/call to a function
+with different return and parameter types than the return type expected and
+arguments intended/passed at the call/branch site.
```shell
$ cargo run --release
Compiling rust-cfi-3 v0.1.0 (/home/rcvalle/rust-cfi-3)
- Finished release [optimized] target(s) in 0.74s
+ Finished release [optimized] target(s) in 0.44s
Running `target/release/rust-cfi-3`
The answer is: 12
With CFI enabled, you should not see the next answer
The next answer is: 14
$
```
-Fig. 8. Build and execution of the modified example with LLVM CFI disabled.
+Fig. 8. Build and execution of Fig. 7 with LLVM CFI disabled.
```shell
-$ RUSTFLAGS="-Cembed-bitcode=yes -Clto -Zsanitizer=cfi" cargo run --release
+$ RUSTFLAGS="-Clinker-plugin-lto -Clinker=clang -Clink-arg=-fuse-ld=lld -Zsanitizer=cfi" cargo run -Zbuild-std -Zbuild-std-features --release --target x86_64-unknown-linux-gnu
+ ...
Compiling rust-cfi-3 v0.1.0 (/home/rcvalle/rust-cfi-3)
- Finished release [optimized] target(s) in 3.40s
- Running `target/release/rust-cfi-3`
+ Finished release [optimized] target(s) in 1m 07s
+ Running `target/x86_64-unknown-linux-gnu/release/rust-cfi-3`
The answer is: 12
With CFI enabled, you should not see the next answer
Illegal instruction
$
```
-Fig. 9. Build and execution of the modified example with LLVM CFI enabled.
+Fig. 9. Build and execution of Fig. 7 with LLVM CFI enabled.
When LLVM CFI is enabled, if there are any attempts to change/hijack control
flow using an indirect branch/call to a function with different return and
parameter types than the return type expected and arguments intended/passed in
the call/branch site, the execution is also terminated (see Fig. 9).
+## Example 4: Redirecting control flow using an indirect branch/call to a function with different return and parameter types across the FFI boundary
+
```ignore (cannot-test-this-because-uses-custom-build)
int
-do_twice(int (*fn)(int), int arg) {
+do_twice(int (*fn)(int), int arg)
+{
return fn(arg) + fn(arg);
}
```
@@ -459,54 +473,49 @@ fn main() {
println!("The next answer is: {}", next_answer);
}
```
-Fig. 11. Another modified example from the [Advanced Functions and
-Closures][rust-book-ch19-05] chapter of the [The Rust Programming
-Language][rust-book] book.
+Fig. 11. Redirecting control flow using an indirect branch/call to a function
+with different return and parameter types than the return type expected and
+arguments intended/passed in the call/branch site, across the FFI boundary.
```shell
$ make
-mkdir -p target/debug
-clang -I. -Isrc -Wall -flto -fvisibility=hidden -c -emit-llvm src/foo.c -o target/debug/libfoo.bc
-llvm-ar rcs target/debug/libfoo.a target/debug/libfoo.bc
-RUSTFLAGS="-L./target/debug -Clinker-plugin-lto -Clinker=clang -Clink-arg=-fuse-ld=lld" cargo build
- Compiling main v0.1.0 (/home/rcvalle/rust-cross-cfi-1)
- Finished dev [unoptimized + debuginfo] target(s) in 0.45s
-$ ./target/debug/main
+mkdir -p target/release
+clang -I. -Isrc -Wall -c src/foo.c -o target/release/libfoo.o
+llvm-ar rcs target/release/libfoo.a target/release/libfoo.o
+RUSTFLAGS="-L./target/release -Clinker=clang -Clink-arg=-fuse-ld=lld" cargo build --release
+ Compiling rust-cfi-4 v0.1.0 (/home/rcvalle/rust-cfi-4)
+ Finished release [optimized] target(s) in 0.49s
+$ ./target/release/rust-cfi-4
The answer is: 12
With CFI enabled, you should not see the next answer
The next answer is: 14
$
```
-Fig. 12. Build and execution of the modified example with LLVM CFI disabled.
+Fig. 12. Build and execution of Figs. 10–11 with LLVM CFI disabled.
```shell
$ make
-mkdir -p target/debug
-clang -I. -Isrc -Wall -flto -fvisibility=hidden -fsanitize=cfi -fsanitize-cfi-icall-normalize-integers -c -emit-llvm src/foo.c -o target/debug/libfoo.bc
-llvm-ar rcs target/debug/libfoo.a target/debug/libfoo.bc
-RUSTFLAGS="-L./target/debug -Clinker-plugin-lto -Clinker=clang -Clink-arg=-fuse-ld=lld -Zsanitizer=cfi -Zsanitizer-cfi-normalize-integers" cargo build
- Compiling main v0.1.0 (/home/rcvalle/rust-cross-cfi-1)
- Finished dev [unoptimized + debuginfo] target(s) in 0.45s
-$ ./target/debug/main
+mkdir -p target/release
+clang -I. -Isrc -Wall -flto -fsanitize=cfi -fsanitize-cfi-icall-experimental-normalize-integers -fvisibility=hidden -c -emit-llvm src/foo.c -o target/release/libfoo.bc
+llvm-ar rcs target/release/libfoo.a target/release/libfoo.bc
+RUSTFLAGS="-L./target/release -Clinker-plugin-lto -Clinker=clang -Clink-arg=-fuse-ld=lld -Zsanitizer=cfi -Zsanitizer-cfi-normalize-integers" cargo build -Zbuild-std -Zbuild-std-features --release --target x86_64-unknown-linux-gnu
+ ...
+ Compiling rust-cfi-4 v0.1.0 (/home/rcvalle/rust-cfi-4)
+ Finished release [optimized] target(s) in 1m 06s
+$ ./target/x86_64-unknown-linux-gnu/release/rust-cfi-4
The answer is: 12
With CFI enabled, you should not see the next answer
Illegal instruction
$
```
-Fig. 13. Build and execution of the modified example with LLVM CFI enabled.
-
-When LLVM CFI is enabled, if there are any attempts to change/hijack control
-flow using an indirect branch/call to a function with different return and
-parameter types than the return type expected and arguments intended/passed in
-the call/branch site, even across the FFI boundary and for extern "C" function
-types indirectly called (i.e., callbacks/function pointers) across the FFI
-boundary, in C or C++ and Rust -compiled code mixed-language binaries, also
-known as “mixed binaries” (i.e., for when C or C++ and Rust -compiled code share
-the same virtual address space), the execution is also terminated (see Fig. 13).
-
-
-[rust-book-ch19-05]: https://doc.rust-lang.org/book/ch19-05-advanced-functions-and-closures.html
-[rust-book]: https://doc.rust-lang.org/book/title-page.html
+Fig. 13. Build and execution of FIgs. 10–11 with LLVM CFI enabled.
+
+When LLVM CFI is enabled, if there are any attempts to redirect control flow
+using an indirect branch/call to a function with different return and parameter
+types than the return type expected and arguments intended/passed in the
+call/branch site, even across the FFI boundary and for extern "C" function types
+indirectly called (i.e., callbacks/function pointers) across the FFI boundary,
+the execution is also terminated (see Fig. 13).
# HWAddressSanitizer
diff --git a/src/doc/unstable-book/src/language-features/closure-track-caller.md b/src/doc/unstable-book/src/language-features/closure-track-caller.md
index c948810d3..997452231 100644
--- a/src/doc/unstable-book/src/language-features/closure-track-caller.md
+++ b/src/doc/unstable-book/src/language-features/closure-track-caller.md
@@ -6,7 +6,7 @@ The tracking issue for this feature is: [#87417]
------------------------
-Allows using the `#[track_caller]` attribute on closures and generators.
-Calls made to the closure or generator will have caller information
+Allows using the `#[track_caller]` attribute on closures and coroutines.
+Calls made to the closure or coroutine will have caller information
available through `std::panic::Location::caller()`, just like using
`#[track_caller]` on a function.
diff --git a/src/doc/unstable-book/src/language-features/coroutines.md b/src/doc/unstable-book/src/language-features/coroutines.md
new file mode 100644
index 000000000..f8e5a22fb
--- /dev/null
+++ b/src/doc/unstable-book/src/language-features/coroutines.md
@@ -0,0 +1,246 @@
+# `coroutines`
+
+The tracking issue for this feature is: [#43122]
+
+[#43122]: https://github.com/rust-lang/rust/issues/43122
+
+------------------------
+
+The `coroutines` feature gate in Rust allows you to define coroutine or
+coroutine literals. A coroutine is a "resumable function" that syntactically
+resembles a closure but compiles to much different semantics in the compiler
+itself. The primary feature of a coroutine is that it can be suspended during
+execution to be resumed at a later date. Coroutines use the `yield` keyword to
+"return", and then the caller can `resume` a coroutine to resume execution just
+after the `yield` keyword.
+
+Coroutines are an extra-unstable feature in the compiler right now. Added in
+[RFC 2033] they're mostly intended right now as a information/constraint
+gathering phase. The intent is that experimentation can happen on the nightly
+compiler before actual stabilization. A further RFC will be required to
+stabilize coroutines and will likely contain at least a few small
+tweaks to the overall design.
+
+[RFC 2033]: https://github.com/rust-lang/rfcs/pull/2033
+
+A syntactical example of a coroutine is:
+
+```rust
+#![feature(coroutines, coroutine_trait)]
+
+use std::ops::{Coroutine, CoroutineState};
+use std::pin::Pin;
+
+fn main() {
+ let mut coroutine = || {
+ yield 1;
+ return "foo"
+ };
+
+ match Pin::new(&mut coroutine).resume(()) {
+ CoroutineState::Yielded(1) => {}
+ _ => panic!("unexpected value from resume"),
+ }
+ match Pin::new(&mut coroutine).resume(()) {
+ CoroutineState::Complete("foo") => {}
+ _ => panic!("unexpected value from resume"),
+ }
+}
+```
+
+Coroutines are closure-like literals which can contain a `yield` statement. The
+`yield` statement takes an optional expression of a value to yield out of the
+coroutine. All coroutine literals implement the `Coroutine` trait in the
+`std::ops` module. The `Coroutine` trait has one main method, `resume`, which
+resumes execution of the coroutine at the previous suspension point.
+
+An example of the control flow of coroutines is that the following example
+prints all numbers in order:
+
+```rust
+#![feature(coroutines, coroutine_trait)]
+
+use std::ops::Coroutine;
+use std::pin::Pin;
+
+fn main() {
+ let mut coroutine = || {
+ println!("2");
+ yield;
+ println!("4");
+ };
+
+ println!("1");
+ Pin::new(&mut coroutine).resume(());
+ println!("3");
+ Pin::new(&mut coroutine).resume(());
+ println!("5");
+}
+```
+
+At this time the main intended use case of coroutines is an implementation
+primitive for async/await syntax, but coroutines will likely be extended to
+ergonomic implementations of iterators and other primitives in the future.
+Feedback on the design and usage is always appreciated!
+
+### The `Coroutine` trait
+
+The `Coroutine` trait in `std::ops` currently looks like:
+
+```rust
+# #![feature(arbitrary_self_types, coroutine_trait)]
+# use std::ops::CoroutineState;
+# use std::pin::Pin;
+
+pub trait Coroutine<R = ()> {
+ type Yield;
+ type Return;
+ fn resume(self: Pin<&mut Self>, resume: R) -> CoroutineState<Self::Yield, Self::Return>;
+}
+```
+
+The `Coroutine::Yield` type is the type of values that can be yielded with the
+`yield` statement. The `Coroutine::Return` type is the returned type of the
+coroutine. This is typically the last expression in a coroutine's definition or
+any value passed to `return` in a coroutine. The `resume` function is the entry
+point for executing the `Coroutine` itself.
+
+The return value of `resume`, `CoroutineState`, looks like:
+
+```rust
+pub enum CoroutineState<Y, R> {
+ Yielded(Y),
+ Complete(R),
+}
+```
+
+The `Yielded` variant indicates that the coroutine can later be resumed. This
+corresponds to a `yield` point in a coroutine. The `Complete` variant indicates
+that the coroutine is complete and cannot be resumed again. Calling `resume`
+after a coroutine has returned `Complete` will likely result in a panic of the
+program.
+
+### Closure-like semantics
+
+The closure-like syntax for coroutines alludes to the fact that they also have
+closure-like semantics. Namely:
+
+* When created, a coroutine executes no code. A closure literal does not
+ actually execute any of the closure's code on construction, and similarly a
+ coroutine literal does not execute any code inside the coroutine when
+ constructed.
+
+* Coroutines can capture outer variables by reference or by move, and this can
+ be tweaked with the `move` keyword at the beginning of the closure. Like
+ closures all coroutines will have an implicit environment which is inferred by
+ the compiler. Outer variables can be moved into a coroutine for use as the
+ coroutine progresses.
+
+* Coroutine literals produce a value with a unique type which implements the
+ `std::ops::Coroutine` trait. This allows actual execution of the coroutine
+ through the `Coroutine::resume` method as well as also naming it in return
+ types and such.
+
+* Traits like `Send` and `Sync` are automatically implemented for a `Coroutine`
+ depending on the captured variables of the environment. Unlike closures,
+ coroutines also depend on variables live across suspension points. This means
+ that although the ambient environment may be `Send` or `Sync`, the coroutine
+ itself may not be due to internal variables live across `yield` points being
+ not-`Send` or not-`Sync`. Note that coroutines do
+ not implement traits like `Copy` or `Clone` automatically.
+
+* Whenever a coroutine is dropped it will drop all captured environment
+ variables.
+
+### Coroutines as state machines
+
+In the compiler, coroutines are currently compiled as state machines. Each
+`yield` expression will correspond to a different state that stores all live
+variables over that suspension point. Resumption of a coroutine will dispatch on
+the current state and then execute internally until a `yield` is reached, at
+which point all state is saved off in the coroutine and a value is returned.
+
+Let's take a look at an example to see what's going on here:
+
+```rust
+#![feature(coroutines, coroutine_trait)]
+
+use std::ops::Coroutine;
+use std::pin::Pin;
+
+fn main() {
+ let ret = "foo";
+ let mut coroutine = move || {
+ yield 1;
+ return ret
+ };
+
+ Pin::new(&mut coroutine).resume(());
+ Pin::new(&mut coroutine).resume(());
+}
+```
+
+This coroutine literal will compile down to something similar to:
+
+```rust
+#![feature(arbitrary_self_types, coroutines, coroutine_trait)]
+
+use std::ops::{Coroutine, CoroutineState};
+use std::pin::Pin;
+
+fn main() {
+ let ret = "foo";
+ let mut coroutine = {
+ enum __Coroutine {
+ Start(&'static str),
+ Yield1(&'static str),
+ Done,
+ }
+
+ impl Coroutine for __Coroutine {
+ type Yield = i32;
+ type Return = &'static str;
+
+ fn resume(mut self: Pin<&mut Self>, resume: ()) -> CoroutineState<i32, &'static str> {
+ use std::mem;
+ match mem::replace(&mut *self, __Coroutine::Done) {
+ __Coroutine::Start(s) => {
+ *self = __Coroutine::Yield1(s);
+ CoroutineState::Yielded(1)
+ }
+
+ __Coroutine::Yield1(s) => {
+ *self = __Coroutine::Done;
+ CoroutineState::Complete(s)
+ }
+
+ __Coroutine::Done => {
+ panic!("coroutine resumed after completion")
+ }
+ }
+ }
+ }
+
+ __Coroutine::Start(ret)
+ };
+
+ Pin::new(&mut coroutine).resume(());
+ Pin::new(&mut coroutine).resume(());
+}
+```
+
+Notably here we can see that the compiler is generating a fresh type,
+`__Coroutine` in this case. This type has a number of states (represented here
+as an `enum`) corresponding to each of the conceptual states of the coroutine.
+At the beginning we're closing over our outer variable `foo` and then that
+variable is also live over the `yield` point, so it's stored in both states.
+
+When the coroutine starts it'll immediately yield 1, but it saves off its state
+just before it does so indicating that it has reached the yield point. Upon
+resuming again we'll execute the `return ret` which returns the `Complete`
+state.
+
+Here we can also note that the `Done` state, if resumed, panics immediately as
+it's invalid to resume a completed coroutine. It's also worth noting that this
+is just a rough desugaring, not a normative specification for what the compiler
+does.
diff --git a/src/doc/unstable-book/src/language-features/diagnostic-namespace.md b/src/doc/unstable-book/src/language-features/diagnostic-namespace.md
new file mode 100644
index 000000000..7c46811a2
--- /dev/null
+++ b/src/doc/unstable-book/src/language-features/diagnostic-namespace.md
@@ -0,0 +1,84 @@
+# `diagnostic_namespace`
+
+The tracking issue for this feature is: [#111996]
+
+[#111996]: https://github.com/rust-lang/rust/issues/111996
+
+------------------------
+
+The `diagnostic_namespace` feature permits customization of compilation errors.
+
+## diagnostic::on_unimplemented
+
+With [#114452] support for `diagnostic::on_unimplemented` was added.
+
+When used on a trait declaration, the following options are available:
+
+* `message` to customize the primary error message
+* `note` to add a customized note message to an error message
+* `label` to customize the label part of the error message
+
+The attribute will hint to the compiler to use these in error messages:
+```rust
+// some library
+#![feature(diagnostic_namespace)]
+
+#[diagnostic::on_unimplemented(
+ message = "cannot insert element",
+ label = "cannot be put into a table",
+ note = "see <link> for more information about the Table api"
+)]
+pub trait Element {
+ // ...
+}
+```
+
+```rust,compile_fail,E0277
+# #![feature(diagnostic_namespace)]
+#
+# #[diagnostic::on_unimplemented(
+# message = "cannot insert element",
+# label = "cannot be put into a table",
+# note = "see <link> for more information about the Table api"
+# )]
+# pub trait Element {
+# // ...
+# }
+# struct Table;
+# impl Table {
+# fn insert<T: Element>(&self, element: T) {
+# // ..
+# }
+# }
+# fn main() {
+# let table = Table;
+# let element = ();
+// user code
+table.insert(element);
+# }
+```
+
+```text
+error[E0277]: cannot insert element
+ --> src/main.rs:24:18
+ |
+24 | table.insert(element);
+ | ------ ^^^^^^^ cannot be put into a table
+ | |
+ | required by a bound introduced by this call
+ |
+ = help: the trait `Element` is not implemented for `<type>`
+ = note: see <link> for more information about the Table api
+note: required by a bound in `Table::insert`
+ --> src/main.rs:15:18
+ |
+15 | fn insert<T: Element>(&self, element: T) {
+ | ^^^^^^^ required by this bound in `Table::insert`
+
+For more information about this error, try `rustc --explain E0277`.
+```
+
+See [RFC 3368] for more information.
+
+[#114452]: https://github.com/rust-lang/rust/pull/114452
+[RFC 3368]: https://github.com/rust-lang/rfcs/blob/master/text/3368-diagnostic-attribute-namespace.md
diff --git a/src/doc/unstable-book/src/language-features/generators.md b/src/doc/unstable-book/src/language-features/generators.md
deleted file mode 100644
index 7b865c9c6..000000000
--- a/src/doc/unstable-book/src/language-features/generators.md
+++ /dev/null
@@ -1,246 +0,0 @@
-# `generators`
-
-The tracking issue for this feature is: [#43122]
-
-[#43122]: https://github.com/rust-lang/rust/issues/43122
-
-------------------------
-
-The `generators` feature gate in Rust allows you to define generator or
-coroutine literals. A generator is a "resumable function" that syntactically
-resembles a closure but compiles to much different semantics in the compiler
-itself. The primary feature of a generator is that it can be suspended during
-execution to be resumed at a later date. Generators use the `yield` keyword to
-"return", and then the caller can `resume` a generator to resume execution just
-after the `yield` keyword.
-
-Generators are an extra-unstable feature in the compiler right now. Added in
-[RFC 2033] they're mostly intended right now as a information/constraint
-gathering phase. The intent is that experimentation can happen on the nightly
-compiler before actual stabilization. A further RFC will be required to
-stabilize generators/coroutines and will likely contain at least a few small
-tweaks to the overall design.
-
-[RFC 2033]: https://github.com/rust-lang/rfcs/pull/2033
-
-A syntactical example of a generator is:
-
-```rust
-#![feature(generators, generator_trait)]
-
-use std::ops::{Generator, GeneratorState};
-use std::pin::Pin;
-
-fn main() {
- let mut generator = || {
- yield 1;
- return "foo"
- };
-
- match Pin::new(&mut generator).resume(()) {
- GeneratorState::Yielded(1) => {}
- _ => panic!("unexpected value from resume"),
- }
- match Pin::new(&mut generator).resume(()) {
- GeneratorState::Complete("foo") => {}
- _ => panic!("unexpected value from resume"),
- }
-}
-```
-
-Generators are closure-like literals which can contain a `yield` statement. The
-`yield` statement takes an optional expression of a value to yield out of the
-generator. All generator literals implement the `Generator` trait in the
-`std::ops` module. The `Generator` trait has one main method, `resume`, which
-resumes execution of the generator at the previous suspension point.
-
-An example of the control flow of generators is that the following example
-prints all numbers in order:
-
-```rust
-#![feature(generators, generator_trait)]
-
-use std::ops::Generator;
-use std::pin::Pin;
-
-fn main() {
- let mut generator = || {
- println!("2");
- yield;
- println!("4");
- };
-
- println!("1");
- Pin::new(&mut generator).resume(());
- println!("3");
- Pin::new(&mut generator).resume(());
- println!("5");
-}
-```
-
-At this time the main intended use case of generators is an implementation
-primitive for async/await syntax, but generators will likely be extended to
-ergonomic implementations of iterators and other primitives in the future.
-Feedback on the design and usage is always appreciated!
-
-### The `Generator` trait
-
-The `Generator` trait in `std::ops` currently looks like:
-
-```rust
-# #![feature(arbitrary_self_types, generator_trait)]
-# use std::ops::GeneratorState;
-# use std::pin::Pin;
-
-pub trait Generator<R = ()> {
- type Yield;
- type Return;
- fn resume(self: Pin<&mut Self>, resume: R) -> GeneratorState<Self::Yield, Self::Return>;
-}
-```
-
-The `Generator::Yield` type is the type of values that can be yielded with the
-`yield` statement. The `Generator::Return` type is the returned type of the
-generator. This is typically the last expression in a generator's definition or
-any value passed to `return` in a generator. The `resume` function is the entry
-point for executing the `Generator` itself.
-
-The return value of `resume`, `GeneratorState`, looks like:
-
-```rust
-pub enum GeneratorState<Y, R> {
- Yielded(Y),
- Complete(R),
-}
-```
-
-The `Yielded` variant indicates that the generator can later be resumed. This
-corresponds to a `yield` point in a generator. The `Complete` variant indicates
-that the generator is complete and cannot be resumed again. Calling `resume`
-after a generator has returned `Complete` will likely result in a panic of the
-program.
-
-### Closure-like semantics
-
-The closure-like syntax for generators alludes to the fact that they also have
-closure-like semantics. Namely:
-
-* When created, a generator executes no code. A closure literal does not
- actually execute any of the closure's code on construction, and similarly a
- generator literal does not execute any code inside the generator when
- constructed.
-
-* Generators can capture outer variables by reference or by move, and this can
- be tweaked with the `move` keyword at the beginning of the closure. Like
- closures all generators will have an implicit environment which is inferred by
- the compiler. Outer variables can be moved into a generator for use as the
- generator progresses.
-
-* Generator literals produce a value with a unique type which implements the
- `std::ops::Generator` trait. This allows actual execution of the generator
- through the `Generator::resume` method as well as also naming it in return
- types and such.
-
-* Traits like `Send` and `Sync` are automatically implemented for a `Generator`
- depending on the captured variables of the environment. Unlike closures,
- generators also depend on variables live across suspension points. This means
- that although the ambient environment may be `Send` or `Sync`, the generator
- itself may not be due to internal variables live across `yield` points being
- not-`Send` or not-`Sync`. Note that generators do
- not implement traits like `Copy` or `Clone` automatically.
-
-* Whenever a generator is dropped it will drop all captured environment
- variables.
-
-### Generators as state machines
-
-In the compiler, generators are currently compiled as state machines. Each
-`yield` expression will correspond to a different state that stores all live
-variables over that suspension point. Resumption of a generator will dispatch on
-the current state and then execute internally until a `yield` is reached, at
-which point all state is saved off in the generator and a value is returned.
-
-Let's take a look at an example to see what's going on here:
-
-```rust
-#![feature(generators, generator_trait)]
-
-use std::ops::Generator;
-use std::pin::Pin;
-
-fn main() {
- let ret = "foo";
- let mut generator = move || {
- yield 1;
- return ret
- };
-
- Pin::new(&mut generator).resume(());
- Pin::new(&mut generator).resume(());
-}
-```
-
-This generator literal will compile down to something similar to:
-
-```rust
-#![feature(arbitrary_self_types, generators, generator_trait)]
-
-use std::ops::{Generator, GeneratorState};
-use std::pin::Pin;
-
-fn main() {
- let ret = "foo";
- let mut generator = {
- enum __Generator {
- Start(&'static str),
- Yield1(&'static str),
- Done,
- }
-
- impl Generator for __Generator {
- type Yield = i32;
- type Return = &'static str;
-
- fn resume(mut self: Pin<&mut Self>, resume: ()) -> GeneratorState<i32, &'static str> {
- use std::mem;
- match mem::replace(&mut *self, __Generator::Done) {
- __Generator::Start(s) => {
- *self = __Generator::Yield1(s);
- GeneratorState::Yielded(1)
- }
-
- __Generator::Yield1(s) => {
- *self = __Generator::Done;
- GeneratorState::Complete(s)
- }
-
- __Generator::Done => {
- panic!("generator resumed after completion")
- }
- }
- }
- }
-
- __Generator::Start(ret)
- };
-
- Pin::new(&mut generator).resume(());
- Pin::new(&mut generator).resume(());
-}
-```
-
-Notably here we can see that the compiler is generating a fresh type,
-`__Generator` in this case. This type has a number of states (represented here
-as an `enum`) corresponding to each of the conceptual states of the generator.
-At the beginning we're closing over our outer variable `foo` and then that
-variable is also live over the `yield` point, so it's stored in both states.
-
-When the generator starts it'll immediately yield 1, but it saves off its state
-just before it does so indicating that it has reached the yield point. Upon
-resuming again we'll execute the `return ret` which returns the `Complete`
-state.
-
-Here we can also note that the `Done` state, if resumed, panics immediately as
-it's invalid to resume a completed generator. It's also worth noting that this
-is just a rough desugaring, not a normative specification for what the compiler
-does.
diff --git a/src/doc/unstable-book/src/language-features/plugin.md b/src/doc/unstable-book/src/language-features/plugin.md
deleted file mode 100644
index 189cc910a..000000000
--- a/src/doc/unstable-book/src/language-features/plugin.md
+++ /dev/null
@@ -1,114 +0,0 @@
-# `plugin`
-
-The tracking issue for this feature is: [#29597]
-
-[#29597]: https://github.com/rust-lang/rust/issues/29597
-
-
-This feature is part of "compiler plugins." It will often be used with the
-`rustc_private` feature.
-
-------------------------
-
-`rustc` can load compiler plugins, which are user-provided libraries that
-extend the compiler's behavior with new lint checks, etc.
-
-A plugin is a dynamic library crate with a designated *registrar* function that
-registers extensions with `rustc`. Other crates can load these extensions using
-the crate attribute `#![plugin(...)]`. See the
-`rustc_driver::plugin` documentation for more about the
-mechanics of defining and loading a plugin.
-
-In the vast majority of cases, a plugin should *only* be used through
-`#![plugin]` and not through an `extern crate` item. Linking a plugin would
-pull in all of librustc_ast and librustc as dependencies of your crate. This is
-generally unwanted unless you are building another plugin.
-
-The usual practice is to put compiler plugins in their own crate, separate from
-any `macro_rules!` macros or ordinary Rust code meant to be used by consumers
-of a library.
-
-# Lint plugins
-
-Plugins can extend [Rust's lint
-infrastructure](../../reference/attributes/diagnostics.md#lint-check-attributes) with
-additional checks for code style, safety, etc. Now let's write a plugin
-[`lint-plugin-test.rs`](https://github.com/rust-lang/rust/blob/master/tests/ui-fulldeps/auxiliary/lint-plugin-test.rs)
-that warns about any item named `lintme`.
-
-```rust,ignore (requires-stage-2)
-#![feature(rustc_private)]
-
-extern crate rustc_ast;
-
-// Load rustc as a plugin to get macros
-extern crate rustc_driver;
-extern crate rustc_lint;
-#[macro_use]
-extern crate rustc_session;
-
-use rustc_ast::ast;
-use rustc_driver::plugin::Registry;
-use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
-
-declare_lint!(TEST_LINT, Warn, "Warn about items named 'lintme'");
-
-declare_lint_pass!(Pass => [TEST_LINT]);
-
-impl EarlyLintPass for Pass {
- fn check_item(&mut self, cx: &EarlyContext, it: &ast::Item) {
- if it.ident.name.as_str() == "lintme" {
- cx.lint(TEST_LINT, "item is named 'lintme'", |lint| lint.set_span(it.span));
- }
- }
-}
-
-#[no_mangle]
-fn __rustc_plugin_registrar(reg: &mut Registry) {
- reg.lint_store.register_lints(&[&TEST_LINT]);
- reg.lint_store.register_early_pass(|| Box::new(Pass));
-}
-```
-
-Then code like
-
-```rust,ignore (requires-plugin)
-#![feature(plugin)]
-#![plugin(lint_plugin_test)]
-
-fn lintme() { }
-```
-
-will produce a compiler warning:
-
-```txt
-foo.rs:4:1: 4:16 warning: item is named 'lintme', #[warn(test_lint)] on by default
-foo.rs:4 fn lintme() { }
- ^~~~~~~~~~~~~~~
-```
-
-The components of a lint plugin are:
-
-* one or more `declare_lint!` invocations, which define static `Lint` structs;
-
-* a struct holding any state needed by the lint pass (here, none);
-
-* a `LintPass`
- implementation defining how to check each syntax element. A single
- `LintPass` may call `span_lint` for several different `Lint`s, but should
- register them all through the `get_lints` method.
-
-Lint passes are syntax traversals, but they run at a late stage of compilation
-where type information is available. `rustc`'s [built-in
-lints](https://github.com/rust-lang/rust/blob/master/compiler/rustc_lint_defs/src/builtin.rs)
-mostly use the same infrastructure as lint plugins, and provide examples of how
-to access type information.
-
-Lints defined by plugins are controlled by the usual [attributes and compiler
-flags](../../reference/attributes/diagnostics.md#lint-check-attributes), e.g.
-`#[allow(test_lint)]` or `-A test-lint`. These identifiers are derived from the
-first argument to `declare_lint!`, with appropriate case and punctuation
-conversion.
-
-You can run `rustc -W help foo.rs` to see a list of lints known to `rustc`,
-including those provided by plugins loaded by `foo.rs`.
diff --git a/src/doc/unstable-book/src/language-features/string-deref-patterns.md b/src/doc/unstable-book/src/language-features/string-deref-patterns.md
new file mode 100644
index 000000000..372383075
--- /dev/null
+++ b/src/doc/unstable-book/src/language-features/string-deref-patterns.md
@@ -0,0 +1,45 @@
+# `string_deref_patterns`
+
+The tracking issue for this feature is: [#87121]
+
+[#87121]: https://github.com/rust-lang/rust/issues/87121
+
+------------------------
+
+This feature permits pattern matching `String` to `&str` through [its `Deref` implementation].
+
+```rust
+#![feature(string_deref_patterns)]
+
+pub enum Value {
+ String(String),
+ Number(u32),
+}
+
+pub fn is_it_the_answer(value: Value) -> bool {
+ match value {
+ Value::String("42") => true,
+ Value::Number(42) => true,
+ _ => false,
+ }
+}
+```
+
+Without this feature other constructs such as match guards have to be used.
+
+```rust
+# pub enum Value {
+# String(String),
+# Number(u32),
+# }
+#
+pub fn is_it_the_answer(value: Value) -> bool {
+ match value {
+ Value::String(s) if s == "42" => true,
+ Value::Number(42) => true,
+ _ => false,
+ }
+}
+```
+
+[its `Deref` implementation]: https://doc.rust-lang.org/std/string/struct.String.html#impl-Deref-for-String
diff --git a/src/doc/unstable-book/src/the-unstable-book.md b/src/doc/unstable-book/src/the-unstable-book.md
index 9090b134d..0f4fb4056 100644
--- a/src/doc/unstable-book/src/the-unstable-book.md
+++ b/src/doc/unstable-book/src/the-unstable-book.md
@@ -5,31 +5,31 @@ each one organized by a "feature flag." That is, when using an unstable
feature of Rust, you must use a flag, like this:
```rust
-#![feature(generators, generator_trait)]
+#![feature(coroutines, coroutine_trait)]
-use std::ops::{Generator, GeneratorState};
+use std::ops::{Coroutine, CoroutineState};
use std::pin::Pin;
fn main() {
- let mut generator = || {
+ let mut coroutine = || {
yield 1;
return "foo"
};
- match Pin::new(&mut generator).resume(()) {
- GeneratorState::Yielded(1) => {}
+ match Pin::new(&mut coroutine).resume(()) {
+ CoroutineState::Yielded(1) => {}
_ => panic!("unexpected value from resume"),
}
- match Pin::new(&mut generator).resume(()) {
- GeneratorState::Complete("foo") => {}
+ match Pin::new(&mut coroutine).resume(()) {
+ CoroutineState::Complete("foo") => {}
_ => panic!("unexpected value from resume"),
}
}
```
-The `generators` feature [has a chapter][generators] describing how to use it.
+The `coroutines` feature [has a chapter][coroutines] describing how to use it.
-[generators]: language-features/generators.md
+[coroutines]: language-features/coroutines.md
Because this documentation relates to unstable features, we make no guarantees
that what is contained here is accurate or up to date. It's developed on a
diff --git a/src/etc/completions/x.py.fish b/src/etc/completions/x.py.fish
index 151c4120d..487969888 100644
--- a/src/etc/completions/x.py.fish
+++ b/src/etc/completions/x.py.fish
@@ -12,10 +12,10 @@ complete -c x.py -n "__fish_use_subcommand" -l keep-stage -d 'stage(s) to keep w
complete -c x.py -n "__fish_use_subcommand" -l keep-stage-std -d 'stage(s) of the standard library to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)' -r -f
complete -c x.py -n "__fish_use_subcommand" -l src -d 'path to the root of the rust checkout' -r -f -a "(__fish_complete_directories)"
complete -c x.py -n "__fish_use_subcommand" -s j -l jobs -d 'number of jobs to run in parallel' -r -f
-complete -c x.py -n "__fish_use_subcommand" -l warnings -d 'if value is deny, will deny warnings if value is warn, will emit warnings otherwise, use the default configured behaviour' -r -f -a "{deny ,warn ,default }"
+complete -c x.py -n "__fish_use_subcommand" -l warnings -d 'if value is deny, will deny warnings if value is warn, will emit warnings otherwise, use the default configured behaviour' -r -f -a "{deny '',warn '',default ''}"
complete -c x.py -n "__fish_use_subcommand" -l error-format -d 'rustc error format' -r -f
-complete -c x.py -n "__fish_use_subcommand" -l color -d 'whether to use color in cargo and rustc output' -r -f -a "{always ,never ,auto }"
-complete -c x.py -n "__fish_use_subcommand" -l llvm-skip-rebuild -d 'whether rebuilding llvm should be skipped, overriding `skip-rebuld` in config.toml' -r -f -a "{true ,false }"
+complete -c x.py -n "__fish_use_subcommand" -l color -d 'whether to use color in cargo and rustc output' -r -f -a "{always '',never '',auto ''}"
+complete -c x.py -n "__fish_use_subcommand" -l llvm-skip-rebuild -d 'whether rebuilding llvm should be skipped, overriding `skip-rebuld` in config.toml' -r -f -a "{true '',false ''}"
complete -c x.py -n "__fish_use_subcommand" -l rust-profile-generate -d 'generate PGO profile with rustc build' -r -F
complete -c x.py -n "__fish_use_subcommand" -l rust-profile-use -d 'use PGO profile for rustc build' -r -F
complete -c x.py -n "__fish_use_subcommand" -l llvm-profile-use -d 'use PGO profile for LLVM build' -r -F
@@ -27,6 +27,8 @@ complete -c x.py -n "__fish_use_subcommand" -l include-default-paths -d 'include
complete -c x.py -n "__fish_use_subcommand" -l dry-run -d 'dry run; don\'t build anything'
complete -c x.py -n "__fish_use_subcommand" -l json-output -d 'use message-format=json'
complete -c x.py -n "__fish_use_subcommand" -l llvm-profile-generate -d 'generate PGO profile with llvm built for rustc'
+complete -c x.py -n "__fish_use_subcommand" -l enable-bolt-settings -d 'Enable BOLT link flags'
+complete -c x.py -n "__fish_use_subcommand" -l skip-stage0-validation -d 'Skip stage0 compiler validation'
complete -c x.py -n "__fish_use_subcommand" -s h -l help -d 'Print help'
complete -c x.py -n "__fish_use_subcommand" -f -a "build" -d 'Compile either the compiler or libraries'
complete -c x.py -n "__fish_use_subcommand" -f -a "check" -d 'Compile either the compiler or libraries, using cargo check'
@@ -56,10 +58,10 @@ complete -c x.py -n "__fish_seen_subcommand_from build" -l keep-stage -d 'stage(
complete -c x.py -n "__fish_seen_subcommand_from build" -l keep-stage-std -d 'stage(s) of the standard library to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)' -r -f
complete -c x.py -n "__fish_seen_subcommand_from build" -l src -d 'path to the root of the rust checkout' -r -f -a "(__fish_complete_directories)"
complete -c x.py -n "__fish_seen_subcommand_from build" -s j -l jobs -d 'number of jobs to run in parallel' -r -f
-complete -c x.py -n "__fish_seen_subcommand_from build" -l warnings -d 'if value is deny, will deny warnings if value is warn, will emit warnings otherwise, use the default configured behaviour' -r -f -a "{deny ,warn ,default }"
+complete -c x.py -n "__fish_seen_subcommand_from build" -l warnings -d 'if value is deny, will deny warnings if value is warn, will emit warnings otherwise, use the default configured behaviour' -r -f -a "{deny '',warn '',default ''}"
complete -c x.py -n "__fish_seen_subcommand_from build" -l error-format -d 'rustc error format' -r -f
-complete -c x.py -n "__fish_seen_subcommand_from build" -l color -d 'whether to use color in cargo and rustc output' -r -f -a "{always ,never ,auto }"
-complete -c x.py -n "__fish_seen_subcommand_from build" -l llvm-skip-rebuild -d 'whether rebuilding llvm should be skipped, overriding `skip-rebuld` in config.toml' -r -f -a "{true ,false }"
+complete -c x.py -n "__fish_seen_subcommand_from build" -l color -d 'whether to use color in cargo and rustc output' -r -f -a "{always '',never '',auto ''}"
+complete -c x.py -n "__fish_seen_subcommand_from build" -l llvm-skip-rebuild -d 'whether rebuilding llvm should be skipped, overriding `skip-rebuld` in config.toml' -r -f -a "{true '',false ''}"
complete -c x.py -n "__fish_seen_subcommand_from build" -l rust-profile-generate -d 'generate PGO profile with rustc build' -r -F
complete -c x.py -n "__fish_seen_subcommand_from build" -l rust-profile-use -d 'use PGO profile for rustc build' -r -F
complete -c x.py -n "__fish_seen_subcommand_from build" -l llvm-profile-use -d 'use PGO profile for LLVM build' -r -F
@@ -71,6 +73,8 @@ complete -c x.py -n "__fish_seen_subcommand_from build" -l include-default-paths
complete -c x.py -n "__fish_seen_subcommand_from build" -l dry-run -d 'dry run; don\'t build anything'
complete -c x.py -n "__fish_seen_subcommand_from build" -l json-output -d 'use message-format=json'
complete -c x.py -n "__fish_seen_subcommand_from build" -l llvm-profile-generate -d 'generate PGO profile with llvm built for rustc'
+complete -c x.py -n "__fish_seen_subcommand_from build" -l enable-bolt-settings -d 'Enable BOLT link flags'
+complete -c x.py -n "__fish_seen_subcommand_from build" -l skip-stage0-validation -d 'Skip stage0 compiler validation'
complete -c x.py -n "__fish_seen_subcommand_from build" -s h -l help -d 'Print help (see more with \'--help\')'
complete -c x.py -n "__fish_seen_subcommand_from check" -l config -d 'TOML configuration file for build' -r -F
complete -c x.py -n "__fish_seen_subcommand_from check" -l build-dir -d 'Build directory, overrides `build.build-dir` in `config.toml`' -r -f -a "(__fish_complete_directories)"
@@ -86,10 +90,10 @@ complete -c x.py -n "__fish_seen_subcommand_from check" -l keep-stage -d 'stage(
complete -c x.py -n "__fish_seen_subcommand_from check" -l keep-stage-std -d 'stage(s) of the standard library to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)' -r -f
complete -c x.py -n "__fish_seen_subcommand_from check" -l src -d 'path to the root of the rust checkout' -r -f -a "(__fish_complete_directories)"
complete -c x.py -n "__fish_seen_subcommand_from check" -s j -l jobs -d 'number of jobs to run in parallel' -r -f
-complete -c x.py -n "__fish_seen_subcommand_from check" -l warnings -d 'if value is deny, will deny warnings if value is warn, will emit warnings otherwise, use the default configured behaviour' -r -f -a "{deny ,warn ,default }"
+complete -c x.py -n "__fish_seen_subcommand_from check" -l warnings -d 'if value is deny, will deny warnings if value is warn, will emit warnings otherwise, use the default configured behaviour' -r -f -a "{deny '',warn '',default ''}"
complete -c x.py -n "__fish_seen_subcommand_from check" -l error-format -d 'rustc error format' -r -f
-complete -c x.py -n "__fish_seen_subcommand_from check" -l color -d 'whether to use color in cargo and rustc output' -r -f -a "{always ,never ,auto }"
-complete -c x.py -n "__fish_seen_subcommand_from check" -l llvm-skip-rebuild -d 'whether rebuilding llvm should be skipped, overriding `skip-rebuld` in config.toml' -r -f -a "{true ,false }"
+complete -c x.py -n "__fish_seen_subcommand_from check" -l color -d 'whether to use color in cargo and rustc output' -r -f -a "{always '',never '',auto ''}"
+complete -c x.py -n "__fish_seen_subcommand_from check" -l llvm-skip-rebuild -d 'whether rebuilding llvm should be skipped, overriding `skip-rebuld` in config.toml' -r -f -a "{true '',false ''}"
complete -c x.py -n "__fish_seen_subcommand_from check" -l rust-profile-generate -d 'generate PGO profile with rustc build' -r -F
complete -c x.py -n "__fish_seen_subcommand_from check" -l rust-profile-use -d 'use PGO profile for rustc build' -r -F
complete -c x.py -n "__fish_seen_subcommand_from check" -l llvm-profile-use -d 'use PGO profile for LLVM build' -r -F
@@ -102,6 +106,8 @@ complete -c x.py -n "__fish_seen_subcommand_from check" -l include-default-paths
complete -c x.py -n "__fish_seen_subcommand_from check" -l dry-run -d 'dry run; don\'t build anything'
complete -c x.py -n "__fish_seen_subcommand_from check" -l json-output -d 'use message-format=json'
complete -c x.py -n "__fish_seen_subcommand_from check" -l llvm-profile-generate -d 'generate PGO profile with llvm built for rustc'
+complete -c x.py -n "__fish_seen_subcommand_from check" -l enable-bolt-settings -d 'Enable BOLT link flags'
+complete -c x.py -n "__fish_seen_subcommand_from check" -l skip-stage0-validation -d 'Skip stage0 compiler validation'
complete -c x.py -n "__fish_seen_subcommand_from check" -s h -l help -d 'Print help (see more with \'--help\')'
complete -c x.py -n "__fish_seen_subcommand_from clippy" -s A -d 'clippy lints to allow' -r
complete -c x.py -n "__fish_seen_subcommand_from clippy" -s D -d 'clippy lints to deny' -r
@@ -121,10 +127,10 @@ complete -c x.py -n "__fish_seen_subcommand_from clippy" -l keep-stage -d 'stage
complete -c x.py -n "__fish_seen_subcommand_from clippy" -l keep-stage-std -d 'stage(s) of the standard library to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)' -r -f
complete -c x.py -n "__fish_seen_subcommand_from clippy" -l src -d 'path to the root of the rust checkout' -r -f -a "(__fish_complete_directories)"
complete -c x.py -n "__fish_seen_subcommand_from clippy" -s j -l jobs -d 'number of jobs to run in parallel' -r -f
-complete -c x.py -n "__fish_seen_subcommand_from clippy" -l warnings -d 'if value is deny, will deny warnings if value is warn, will emit warnings otherwise, use the default configured behaviour' -r -f -a "{deny ,warn ,default }"
+complete -c x.py -n "__fish_seen_subcommand_from clippy" -l warnings -d 'if value is deny, will deny warnings if value is warn, will emit warnings otherwise, use the default configured behaviour' -r -f -a "{deny '',warn '',default ''}"
complete -c x.py -n "__fish_seen_subcommand_from clippy" -l error-format -d 'rustc error format' -r -f
-complete -c x.py -n "__fish_seen_subcommand_from clippy" -l color -d 'whether to use color in cargo and rustc output' -r -f -a "{always ,never ,auto }"
-complete -c x.py -n "__fish_seen_subcommand_from clippy" -l llvm-skip-rebuild -d 'whether rebuilding llvm should be skipped, overriding `skip-rebuld` in config.toml' -r -f -a "{true ,false }"
+complete -c x.py -n "__fish_seen_subcommand_from clippy" -l color -d 'whether to use color in cargo and rustc output' -r -f -a "{always '',never '',auto ''}"
+complete -c x.py -n "__fish_seen_subcommand_from clippy" -l llvm-skip-rebuild -d 'whether rebuilding llvm should be skipped, overriding `skip-rebuld` in config.toml' -r -f -a "{true '',false ''}"
complete -c x.py -n "__fish_seen_subcommand_from clippy" -l rust-profile-generate -d 'generate PGO profile with rustc build' -r -F
complete -c x.py -n "__fish_seen_subcommand_from clippy" -l rust-profile-use -d 'use PGO profile for rustc build' -r -F
complete -c x.py -n "__fish_seen_subcommand_from clippy" -l llvm-profile-use -d 'use PGO profile for LLVM build' -r -F
@@ -137,6 +143,8 @@ complete -c x.py -n "__fish_seen_subcommand_from clippy" -l include-default-path
complete -c x.py -n "__fish_seen_subcommand_from clippy" -l dry-run -d 'dry run; don\'t build anything'
complete -c x.py -n "__fish_seen_subcommand_from clippy" -l json-output -d 'use message-format=json'
complete -c x.py -n "__fish_seen_subcommand_from clippy" -l llvm-profile-generate -d 'generate PGO profile with llvm built for rustc'
+complete -c x.py -n "__fish_seen_subcommand_from clippy" -l enable-bolt-settings -d 'Enable BOLT link flags'
+complete -c x.py -n "__fish_seen_subcommand_from clippy" -l skip-stage0-validation -d 'Skip stage0 compiler validation'
complete -c x.py -n "__fish_seen_subcommand_from clippy" -s h -l help -d 'Print help (see more with \'--help\')'
complete -c x.py -n "__fish_seen_subcommand_from fix" -l config -d 'TOML configuration file for build' -r -F
complete -c x.py -n "__fish_seen_subcommand_from fix" -l build-dir -d 'Build directory, overrides `build.build-dir` in `config.toml`' -r -f -a "(__fish_complete_directories)"
@@ -152,10 +160,10 @@ complete -c x.py -n "__fish_seen_subcommand_from fix" -l keep-stage -d 'stage(s)
complete -c x.py -n "__fish_seen_subcommand_from fix" -l keep-stage-std -d 'stage(s) of the standard library to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)' -r -f
complete -c x.py -n "__fish_seen_subcommand_from fix" -l src -d 'path to the root of the rust checkout' -r -f -a "(__fish_complete_directories)"
complete -c x.py -n "__fish_seen_subcommand_from fix" -s j -l jobs -d 'number of jobs to run in parallel' -r -f
-complete -c x.py -n "__fish_seen_subcommand_from fix" -l warnings -d 'if value is deny, will deny warnings if value is warn, will emit warnings otherwise, use the default configured behaviour' -r -f -a "{deny ,warn ,default }"
+complete -c x.py -n "__fish_seen_subcommand_from fix" -l warnings -d 'if value is deny, will deny warnings if value is warn, will emit warnings otherwise, use the default configured behaviour' -r -f -a "{deny '',warn '',default ''}"
complete -c x.py -n "__fish_seen_subcommand_from fix" -l error-format -d 'rustc error format' -r -f
-complete -c x.py -n "__fish_seen_subcommand_from fix" -l color -d 'whether to use color in cargo and rustc output' -r -f -a "{always ,never ,auto }"
-complete -c x.py -n "__fish_seen_subcommand_from fix" -l llvm-skip-rebuild -d 'whether rebuilding llvm should be skipped, overriding `skip-rebuld` in config.toml' -r -f -a "{true ,false }"
+complete -c x.py -n "__fish_seen_subcommand_from fix" -l color -d 'whether to use color in cargo and rustc output' -r -f -a "{always '',never '',auto ''}"
+complete -c x.py -n "__fish_seen_subcommand_from fix" -l llvm-skip-rebuild -d 'whether rebuilding llvm should be skipped, overriding `skip-rebuld` in config.toml' -r -f -a "{true '',false ''}"
complete -c x.py -n "__fish_seen_subcommand_from fix" -l rust-profile-generate -d 'generate PGO profile with rustc build' -r -F
complete -c x.py -n "__fish_seen_subcommand_from fix" -l rust-profile-use -d 'use PGO profile for rustc build' -r -F
complete -c x.py -n "__fish_seen_subcommand_from fix" -l llvm-profile-use -d 'use PGO profile for LLVM build' -r -F
@@ -167,6 +175,8 @@ complete -c x.py -n "__fish_seen_subcommand_from fix" -l include-default-paths -
complete -c x.py -n "__fish_seen_subcommand_from fix" -l dry-run -d 'dry run; don\'t build anything'
complete -c x.py -n "__fish_seen_subcommand_from fix" -l json-output -d 'use message-format=json'
complete -c x.py -n "__fish_seen_subcommand_from fix" -l llvm-profile-generate -d 'generate PGO profile with llvm built for rustc'
+complete -c x.py -n "__fish_seen_subcommand_from fix" -l enable-bolt-settings -d 'Enable BOLT link flags'
+complete -c x.py -n "__fish_seen_subcommand_from fix" -l skip-stage0-validation -d 'Skip stage0 compiler validation'
complete -c x.py -n "__fish_seen_subcommand_from fix" -s h -l help -d 'Print help (see more with \'--help\')'
complete -c x.py -n "__fish_seen_subcommand_from fmt" -l config -d 'TOML configuration file for build' -r -F
complete -c x.py -n "__fish_seen_subcommand_from fmt" -l build-dir -d 'Build directory, overrides `build.build-dir` in `config.toml`' -r -f -a "(__fish_complete_directories)"
@@ -182,10 +192,10 @@ complete -c x.py -n "__fish_seen_subcommand_from fmt" -l keep-stage -d 'stage(s)
complete -c x.py -n "__fish_seen_subcommand_from fmt" -l keep-stage-std -d 'stage(s) of the standard library to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)' -r -f
complete -c x.py -n "__fish_seen_subcommand_from fmt" -l src -d 'path to the root of the rust checkout' -r -f -a "(__fish_complete_directories)"
complete -c x.py -n "__fish_seen_subcommand_from fmt" -s j -l jobs -d 'number of jobs to run in parallel' -r -f
-complete -c x.py -n "__fish_seen_subcommand_from fmt" -l warnings -d 'if value is deny, will deny warnings if value is warn, will emit warnings otherwise, use the default configured behaviour' -r -f -a "{deny ,warn ,default }"
+complete -c x.py -n "__fish_seen_subcommand_from fmt" -l warnings -d 'if value is deny, will deny warnings if value is warn, will emit warnings otherwise, use the default configured behaviour' -r -f -a "{deny '',warn '',default ''}"
complete -c x.py -n "__fish_seen_subcommand_from fmt" -l error-format -d 'rustc error format' -r -f
-complete -c x.py -n "__fish_seen_subcommand_from fmt" -l color -d 'whether to use color in cargo and rustc output' -r -f -a "{always ,never ,auto }"
-complete -c x.py -n "__fish_seen_subcommand_from fmt" -l llvm-skip-rebuild -d 'whether rebuilding llvm should be skipped, overriding `skip-rebuld` in config.toml' -r -f -a "{true ,false }"
+complete -c x.py -n "__fish_seen_subcommand_from fmt" -l color -d 'whether to use color in cargo and rustc output' -r -f -a "{always '',never '',auto ''}"
+complete -c x.py -n "__fish_seen_subcommand_from fmt" -l llvm-skip-rebuild -d 'whether rebuilding llvm should be skipped, overriding `skip-rebuld` in config.toml' -r -f -a "{true '',false ''}"
complete -c x.py -n "__fish_seen_subcommand_from fmt" -l rust-profile-generate -d 'generate PGO profile with rustc build' -r -F
complete -c x.py -n "__fish_seen_subcommand_from fmt" -l rust-profile-use -d 'use PGO profile for rustc build' -r -F
complete -c x.py -n "__fish_seen_subcommand_from fmt" -l llvm-profile-use -d 'use PGO profile for LLVM build' -r -F
@@ -198,6 +208,8 @@ complete -c x.py -n "__fish_seen_subcommand_from fmt" -l include-default-paths -
complete -c x.py -n "__fish_seen_subcommand_from fmt" -l dry-run -d 'dry run; don\'t build anything'
complete -c x.py -n "__fish_seen_subcommand_from fmt" -l json-output -d 'use message-format=json'
complete -c x.py -n "__fish_seen_subcommand_from fmt" -l llvm-profile-generate -d 'generate PGO profile with llvm built for rustc'
+complete -c x.py -n "__fish_seen_subcommand_from fmt" -l enable-bolt-settings -d 'Enable BOLT link flags'
+complete -c x.py -n "__fish_seen_subcommand_from fmt" -l skip-stage0-validation -d 'Skip stage0 compiler validation'
complete -c x.py -n "__fish_seen_subcommand_from fmt" -s h -l help -d 'Print help (see more with \'--help\')'
complete -c x.py -n "__fish_seen_subcommand_from doc" -l config -d 'TOML configuration file for build' -r -F
complete -c x.py -n "__fish_seen_subcommand_from doc" -l build-dir -d 'Build directory, overrides `build.build-dir` in `config.toml`' -r -f -a "(__fish_complete_directories)"
@@ -213,10 +225,10 @@ complete -c x.py -n "__fish_seen_subcommand_from doc" -l keep-stage -d 'stage(s)
complete -c x.py -n "__fish_seen_subcommand_from doc" -l keep-stage-std -d 'stage(s) of the standard library to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)' -r -f
complete -c x.py -n "__fish_seen_subcommand_from doc" -l src -d 'path to the root of the rust checkout' -r -f -a "(__fish_complete_directories)"
complete -c x.py -n "__fish_seen_subcommand_from doc" -s j -l jobs -d 'number of jobs to run in parallel' -r -f
-complete -c x.py -n "__fish_seen_subcommand_from doc" -l warnings -d 'if value is deny, will deny warnings if value is warn, will emit warnings otherwise, use the default configured behaviour' -r -f -a "{deny ,warn ,default }"
+complete -c x.py -n "__fish_seen_subcommand_from doc" -l warnings -d 'if value is deny, will deny warnings if value is warn, will emit warnings otherwise, use the default configured behaviour' -r -f -a "{deny '',warn '',default ''}"
complete -c x.py -n "__fish_seen_subcommand_from doc" -l error-format -d 'rustc error format' -r -f
-complete -c x.py -n "__fish_seen_subcommand_from doc" -l color -d 'whether to use color in cargo and rustc output' -r -f -a "{always ,never ,auto }"
-complete -c x.py -n "__fish_seen_subcommand_from doc" -l llvm-skip-rebuild -d 'whether rebuilding llvm should be skipped, overriding `skip-rebuld` in config.toml' -r -f -a "{true ,false }"
+complete -c x.py -n "__fish_seen_subcommand_from doc" -l color -d 'whether to use color in cargo and rustc output' -r -f -a "{always '',never '',auto ''}"
+complete -c x.py -n "__fish_seen_subcommand_from doc" -l llvm-skip-rebuild -d 'whether rebuilding llvm should be skipped, overriding `skip-rebuld` in config.toml' -r -f -a "{true '',false ''}"
complete -c x.py -n "__fish_seen_subcommand_from doc" -l rust-profile-generate -d 'generate PGO profile with rustc build' -r -F
complete -c x.py -n "__fish_seen_subcommand_from doc" -l rust-profile-use -d 'use PGO profile for rustc build' -r -F
complete -c x.py -n "__fish_seen_subcommand_from doc" -l llvm-profile-use -d 'use PGO profile for LLVM build' -r -F
@@ -230,6 +242,8 @@ complete -c x.py -n "__fish_seen_subcommand_from doc" -l include-default-paths -
complete -c x.py -n "__fish_seen_subcommand_from doc" -l dry-run -d 'dry run; don\'t build anything'
complete -c x.py -n "__fish_seen_subcommand_from doc" -l json-output -d 'use message-format=json'
complete -c x.py -n "__fish_seen_subcommand_from doc" -l llvm-profile-generate -d 'generate PGO profile with llvm built for rustc'
+complete -c x.py -n "__fish_seen_subcommand_from doc" -l enable-bolt-settings -d 'Enable BOLT link flags'
+complete -c x.py -n "__fish_seen_subcommand_from doc" -l skip-stage0-validation -d 'Skip stage0 compiler validation'
complete -c x.py -n "__fish_seen_subcommand_from doc" -s h -l help -d 'Print help (see more with \'--help\')'
complete -c x.py -n "__fish_seen_subcommand_from test" -l skip -d 'skips tests matching SUBSTRING, if supported by test tool. May be passed multiple times' -r -F
complete -c x.py -n "__fish_seen_subcommand_from test" -l test-args -d 'extra arguments to be passed for the test tool being used (e.g. libtest, compiletest or rustdoc)' -r
@@ -251,10 +265,10 @@ complete -c x.py -n "__fish_seen_subcommand_from test" -l keep-stage -d 'stage(s
complete -c x.py -n "__fish_seen_subcommand_from test" -l keep-stage-std -d 'stage(s) of the standard library to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)' -r -f
complete -c x.py -n "__fish_seen_subcommand_from test" -l src -d 'path to the root of the rust checkout' -r -f -a "(__fish_complete_directories)"
complete -c x.py -n "__fish_seen_subcommand_from test" -s j -l jobs -d 'number of jobs to run in parallel' -r -f
-complete -c x.py -n "__fish_seen_subcommand_from test" -l warnings -d 'if value is deny, will deny warnings if value is warn, will emit warnings otherwise, use the default configured behaviour' -r -f -a "{deny ,warn ,default }"
+complete -c x.py -n "__fish_seen_subcommand_from test" -l warnings -d 'if value is deny, will deny warnings if value is warn, will emit warnings otherwise, use the default configured behaviour' -r -f -a "{deny '',warn '',default ''}"
complete -c x.py -n "__fish_seen_subcommand_from test" -l error-format -d 'rustc error format' -r -f
-complete -c x.py -n "__fish_seen_subcommand_from test" -l color -d 'whether to use color in cargo and rustc output' -r -f -a "{always ,never ,auto }"
-complete -c x.py -n "__fish_seen_subcommand_from test" -l llvm-skip-rebuild -d 'whether rebuilding llvm should be skipped, overriding `skip-rebuld` in config.toml' -r -f -a "{true ,false }"
+complete -c x.py -n "__fish_seen_subcommand_from test" -l color -d 'whether to use color in cargo and rustc output' -r -f -a "{always '',never '',auto ''}"
+complete -c x.py -n "__fish_seen_subcommand_from test" -l llvm-skip-rebuild -d 'whether rebuilding llvm should be skipped, overriding `skip-rebuld` in config.toml' -r -f -a "{true '',false ''}"
complete -c x.py -n "__fish_seen_subcommand_from test" -l rust-profile-generate -d 'generate PGO profile with rustc build' -r -F
complete -c x.py -n "__fish_seen_subcommand_from test" -l rust-profile-use -d 'use PGO profile for rustc build' -r -F
complete -c x.py -n "__fish_seen_subcommand_from test" -l llvm-profile-use -d 'use PGO profile for LLVM build' -r -F
@@ -273,6 +287,8 @@ complete -c x.py -n "__fish_seen_subcommand_from test" -l include-default-paths
complete -c x.py -n "__fish_seen_subcommand_from test" -l dry-run -d 'dry run; don\'t build anything'
complete -c x.py -n "__fish_seen_subcommand_from test" -l json-output -d 'use message-format=json'
complete -c x.py -n "__fish_seen_subcommand_from test" -l llvm-profile-generate -d 'generate PGO profile with llvm built for rustc'
+complete -c x.py -n "__fish_seen_subcommand_from test" -l enable-bolt-settings -d 'Enable BOLT link flags'
+complete -c x.py -n "__fish_seen_subcommand_from test" -l skip-stage0-validation -d 'Skip stage0 compiler validation'
complete -c x.py -n "__fish_seen_subcommand_from test" -s h -l help -d 'Print help (see more with \'--help\')'
complete -c x.py -n "__fish_seen_subcommand_from bench" -l test-args -r
complete -c x.py -n "__fish_seen_subcommand_from bench" -l config -d 'TOML configuration file for build' -r -F
@@ -289,10 +305,10 @@ complete -c x.py -n "__fish_seen_subcommand_from bench" -l keep-stage -d 'stage(
complete -c x.py -n "__fish_seen_subcommand_from bench" -l keep-stage-std -d 'stage(s) of the standard library to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)' -r -f
complete -c x.py -n "__fish_seen_subcommand_from bench" -l src -d 'path to the root of the rust checkout' -r -f -a "(__fish_complete_directories)"
complete -c x.py -n "__fish_seen_subcommand_from bench" -s j -l jobs -d 'number of jobs to run in parallel' -r -f
-complete -c x.py -n "__fish_seen_subcommand_from bench" -l warnings -d 'if value is deny, will deny warnings if value is warn, will emit warnings otherwise, use the default configured behaviour' -r -f -a "{deny ,warn ,default }"
+complete -c x.py -n "__fish_seen_subcommand_from bench" -l warnings -d 'if value is deny, will deny warnings if value is warn, will emit warnings otherwise, use the default configured behaviour' -r -f -a "{deny '',warn '',default ''}"
complete -c x.py -n "__fish_seen_subcommand_from bench" -l error-format -d 'rustc error format' -r -f
-complete -c x.py -n "__fish_seen_subcommand_from bench" -l color -d 'whether to use color in cargo and rustc output' -r -f -a "{always ,never ,auto }"
-complete -c x.py -n "__fish_seen_subcommand_from bench" -l llvm-skip-rebuild -d 'whether rebuilding llvm should be skipped, overriding `skip-rebuld` in config.toml' -r -f -a "{true ,false }"
+complete -c x.py -n "__fish_seen_subcommand_from bench" -l color -d 'whether to use color in cargo and rustc output' -r -f -a "{always '',never '',auto ''}"
+complete -c x.py -n "__fish_seen_subcommand_from bench" -l llvm-skip-rebuild -d 'whether rebuilding llvm should be skipped, overriding `skip-rebuld` in config.toml' -r -f -a "{true '',false ''}"
complete -c x.py -n "__fish_seen_subcommand_from bench" -l rust-profile-generate -d 'generate PGO profile with rustc build' -r -F
complete -c x.py -n "__fish_seen_subcommand_from bench" -l rust-profile-use -d 'use PGO profile for rustc build' -r -F
complete -c x.py -n "__fish_seen_subcommand_from bench" -l llvm-profile-use -d 'use PGO profile for LLVM build' -r -F
@@ -304,6 +320,8 @@ complete -c x.py -n "__fish_seen_subcommand_from bench" -l include-default-paths
complete -c x.py -n "__fish_seen_subcommand_from bench" -l dry-run -d 'dry run; don\'t build anything'
complete -c x.py -n "__fish_seen_subcommand_from bench" -l json-output -d 'use message-format=json'
complete -c x.py -n "__fish_seen_subcommand_from bench" -l llvm-profile-generate -d 'generate PGO profile with llvm built for rustc'
+complete -c x.py -n "__fish_seen_subcommand_from bench" -l enable-bolt-settings -d 'Enable BOLT link flags'
+complete -c x.py -n "__fish_seen_subcommand_from bench" -l skip-stage0-validation -d 'Skip stage0 compiler validation'
complete -c x.py -n "__fish_seen_subcommand_from bench" -s h -l help -d 'Print help'
complete -c x.py -n "__fish_seen_subcommand_from clean" -l stage -d 'Clean a specific stage without touching other artifacts. By default, every stage is cleaned if this option is not used' -r
complete -c x.py -n "__fish_seen_subcommand_from clean" -l config -d 'TOML configuration file for build' -r -F
@@ -319,10 +337,10 @@ complete -c x.py -n "__fish_seen_subcommand_from clean" -l keep-stage -d 'stage(
complete -c x.py -n "__fish_seen_subcommand_from clean" -l keep-stage-std -d 'stage(s) of the standard library to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)' -r -f
complete -c x.py -n "__fish_seen_subcommand_from clean" -l src -d 'path to the root of the rust checkout' -r -f -a "(__fish_complete_directories)"
complete -c x.py -n "__fish_seen_subcommand_from clean" -s j -l jobs -d 'number of jobs to run in parallel' -r -f
-complete -c x.py -n "__fish_seen_subcommand_from clean" -l warnings -d 'if value is deny, will deny warnings if value is warn, will emit warnings otherwise, use the default configured behaviour' -r -f -a "{deny ,warn ,default }"
+complete -c x.py -n "__fish_seen_subcommand_from clean" -l warnings -d 'if value is deny, will deny warnings if value is warn, will emit warnings otherwise, use the default configured behaviour' -r -f -a "{deny '',warn '',default ''}"
complete -c x.py -n "__fish_seen_subcommand_from clean" -l error-format -d 'rustc error format' -r -f
-complete -c x.py -n "__fish_seen_subcommand_from clean" -l color -d 'whether to use color in cargo and rustc output' -r -f -a "{always ,never ,auto }"
-complete -c x.py -n "__fish_seen_subcommand_from clean" -l llvm-skip-rebuild -d 'whether rebuilding llvm should be skipped, overriding `skip-rebuld` in config.toml' -r -f -a "{true ,false }"
+complete -c x.py -n "__fish_seen_subcommand_from clean" -l color -d 'whether to use color in cargo and rustc output' -r -f -a "{always '',never '',auto ''}"
+complete -c x.py -n "__fish_seen_subcommand_from clean" -l llvm-skip-rebuild -d 'whether rebuilding llvm should be skipped, overriding `skip-rebuld` in config.toml' -r -f -a "{true '',false ''}"
complete -c x.py -n "__fish_seen_subcommand_from clean" -l rust-profile-generate -d 'generate PGO profile with rustc build' -r -F
complete -c x.py -n "__fish_seen_subcommand_from clean" -l rust-profile-use -d 'use PGO profile for rustc build' -r -F
complete -c x.py -n "__fish_seen_subcommand_from clean" -l llvm-profile-use -d 'use PGO profile for LLVM build' -r -F
@@ -335,6 +353,8 @@ complete -c x.py -n "__fish_seen_subcommand_from clean" -l include-default-paths
complete -c x.py -n "__fish_seen_subcommand_from clean" -l dry-run -d 'dry run; don\'t build anything'
complete -c x.py -n "__fish_seen_subcommand_from clean" -l json-output -d 'use message-format=json'
complete -c x.py -n "__fish_seen_subcommand_from clean" -l llvm-profile-generate -d 'generate PGO profile with llvm built for rustc'
+complete -c x.py -n "__fish_seen_subcommand_from clean" -l enable-bolt-settings -d 'Enable BOLT link flags'
+complete -c x.py -n "__fish_seen_subcommand_from clean" -l skip-stage0-validation -d 'Skip stage0 compiler validation'
complete -c x.py -n "__fish_seen_subcommand_from clean" -s h -l help -d 'Print help'
complete -c x.py -n "__fish_seen_subcommand_from dist" -l config -d 'TOML configuration file for build' -r -F
complete -c x.py -n "__fish_seen_subcommand_from dist" -l build-dir -d 'Build directory, overrides `build.build-dir` in `config.toml`' -r -f -a "(__fish_complete_directories)"
@@ -350,10 +370,10 @@ complete -c x.py -n "__fish_seen_subcommand_from dist" -l keep-stage -d 'stage(s
complete -c x.py -n "__fish_seen_subcommand_from dist" -l keep-stage-std -d 'stage(s) of the standard library to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)' -r -f
complete -c x.py -n "__fish_seen_subcommand_from dist" -l src -d 'path to the root of the rust checkout' -r -f -a "(__fish_complete_directories)"
complete -c x.py -n "__fish_seen_subcommand_from dist" -s j -l jobs -d 'number of jobs to run in parallel' -r -f
-complete -c x.py -n "__fish_seen_subcommand_from dist" -l warnings -d 'if value is deny, will deny warnings if value is warn, will emit warnings otherwise, use the default configured behaviour' -r -f -a "{deny ,warn ,default }"
+complete -c x.py -n "__fish_seen_subcommand_from dist" -l warnings -d 'if value is deny, will deny warnings if value is warn, will emit warnings otherwise, use the default configured behaviour' -r -f -a "{deny '',warn '',default ''}"
complete -c x.py -n "__fish_seen_subcommand_from dist" -l error-format -d 'rustc error format' -r -f
-complete -c x.py -n "__fish_seen_subcommand_from dist" -l color -d 'whether to use color in cargo and rustc output' -r -f -a "{always ,never ,auto }"
-complete -c x.py -n "__fish_seen_subcommand_from dist" -l llvm-skip-rebuild -d 'whether rebuilding llvm should be skipped, overriding `skip-rebuld` in config.toml' -r -f -a "{true ,false }"
+complete -c x.py -n "__fish_seen_subcommand_from dist" -l color -d 'whether to use color in cargo and rustc output' -r -f -a "{always '',never '',auto ''}"
+complete -c x.py -n "__fish_seen_subcommand_from dist" -l llvm-skip-rebuild -d 'whether rebuilding llvm should be skipped, overriding `skip-rebuld` in config.toml' -r -f -a "{true '',false ''}"
complete -c x.py -n "__fish_seen_subcommand_from dist" -l rust-profile-generate -d 'generate PGO profile with rustc build' -r -F
complete -c x.py -n "__fish_seen_subcommand_from dist" -l rust-profile-use -d 'use PGO profile for rustc build' -r -F
complete -c x.py -n "__fish_seen_subcommand_from dist" -l llvm-profile-use -d 'use PGO profile for LLVM build' -r -F
@@ -365,6 +385,8 @@ complete -c x.py -n "__fish_seen_subcommand_from dist" -l include-default-paths
complete -c x.py -n "__fish_seen_subcommand_from dist" -l dry-run -d 'dry run; don\'t build anything'
complete -c x.py -n "__fish_seen_subcommand_from dist" -l json-output -d 'use message-format=json'
complete -c x.py -n "__fish_seen_subcommand_from dist" -l llvm-profile-generate -d 'generate PGO profile with llvm built for rustc'
+complete -c x.py -n "__fish_seen_subcommand_from dist" -l enable-bolt-settings -d 'Enable BOLT link flags'
+complete -c x.py -n "__fish_seen_subcommand_from dist" -l skip-stage0-validation -d 'Skip stage0 compiler validation'
complete -c x.py -n "__fish_seen_subcommand_from dist" -s h -l help -d 'Print help'
complete -c x.py -n "__fish_seen_subcommand_from install" -l config -d 'TOML configuration file for build' -r -F
complete -c x.py -n "__fish_seen_subcommand_from install" -l build-dir -d 'Build directory, overrides `build.build-dir` in `config.toml`' -r -f -a "(__fish_complete_directories)"
@@ -380,10 +402,10 @@ complete -c x.py -n "__fish_seen_subcommand_from install" -l keep-stage -d 'stag
complete -c x.py -n "__fish_seen_subcommand_from install" -l keep-stage-std -d 'stage(s) of the standard library to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)' -r -f
complete -c x.py -n "__fish_seen_subcommand_from install" -l src -d 'path to the root of the rust checkout' -r -f -a "(__fish_complete_directories)"
complete -c x.py -n "__fish_seen_subcommand_from install" -s j -l jobs -d 'number of jobs to run in parallel' -r -f
-complete -c x.py -n "__fish_seen_subcommand_from install" -l warnings -d 'if value is deny, will deny warnings if value is warn, will emit warnings otherwise, use the default configured behaviour' -r -f -a "{deny ,warn ,default }"
+complete -c x.py -n "__fish_seen_subcommand_from install" -l warnings -d 'if value is deny, will deny warnings if value is warn, will emit warnings otherwise, use the default configured behaviour' -r -f -a "{deny '',warn '',default ''}"
complete -c x.py -n "__fish_seen_subcommand_from install" -l error-format -d 'rustc error format' -r -f
-complete -c x.py -n "__fish_seen_subcommand_from install" -l color -d 'whether to use color in cargo and rustc output' -r -f -a "{always ,never ,auto }"
-complete -c x.py -n "__fish_seen_subcommand_from install" -l llvm-skip-rebuild -d 'whether rebuilding llvm should be skipped, overriding `skip-rebuld` in config.toml' -r -f -a "{true ,false }"
+complete -c x.py -n "__fish_seen_subcommand_from install" -l color -d 'whether to use color in cargo and rustc output' -r -f -a "{always '',never '',auto ''}"
+complete -c x.py -n "__fish_seen_subcommand_from install" -l llvm-skip-rebuild -d 'whether rebuilding llvm should be skipped, overriding `skip-rebuld` in config.toml' -r -f -a "{true '',false ''}"
complete -c x.py -n "__fish_seen_subcommand_from install" -l rust-profile-generate -d 'generate PGO profile with rustc build' -r -F
complete -c x.py -n "__fish_seen_subcommand_from install" -l rust-profile-use -d 'use PGO profile for rustc build' -r -F
complete -c x.py -n "__fish_seen_subcommand_from install" -l llvm-profile-use -d 'use PGO profile for LLVM build' -r -F
@@ -395,6 +417,8 @@ complete -c x.py -n "__fish_seen_subcommand_from install" -l include-default-pat
complete -c x.py -n "__fish_seen_subcommand_from install" -l dry-run -d 'dry run; don\'t build anything'
complete -c x.py -n "__fish_seen_subcommand_from install" -l json-output -d 'use message-format=json'
complete -c x.py -n "__fish_seen_subcommand_from install" -l llvm-profile-generate -d 'generate PGO profile with llvm built for rustc'
+complete -c x.py -n "__fish_seen_subcommand_from install" -l enable-bolt-settings -d 'Enable BOLT link flags'
+complete -c x.py -n "__fish_seen_subcommand_from install" -l skip-stage0-validation -d 'Skip stage0 compiler validation'
complete -c x.py -n "__fish_seen_subcommand_from install" -s h -l help -d 'Print help'
complete -c x.py -n "__fish_seen_subcommand_from run" -l args -d 'arguments for the tool' -r
complete -c x.py -n "__fish_seen_subcommand_from run" -l config -d 'TOML configuration file for build' -r -F
@@ -411,10 +435,10 @@ complete -c x.py -n "__fish_seen_subcommand_from run" -l keep-stage -d 'stage(s)
complete -c x.py -n "__fish_seen_subcommand_from run" -l keep-stage-std -d 'stage(s) of the standard library to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)' -r -f
complete -c x.py -n "__fish_seen_subcommand_from run" -l src -d 'path to the root of the rust checkout' -r -f -a "(__fish_complete_directories)"
complete -c x.py -n "__fish_seen_subcommand_from run" -s j -l jobs -d 'number of jobs to run in parallel' -r -f
-complete -c x.py -n "__fish_seen_subcommand_from run" -l warnings -d 'if value is deny, will deny warnings if value is warn, will emit warnings otherwise, use the default configured behaviour' -r -f -a "{deny ,warn ,default }"
+complete -c x.py -n "__fish_seen_subcommand_from run" -l warnings -d 'if value is deny, will deny warnings if value is warn, will emit warnings otherwise, use the default configured behaviour' -r -f -a "{deny '',warn '',default ''}"
complete -c x.py -n "__fish_seen_subcommand_from run" -l error-format -d 'rustc error format' -r -f
-complete -c x.py -n "__fish_seen_subcommand_from run" -l color -d 'whether to use color in cargo and rustc output' -r -f -a "{always ,never ,auto }"
-complete -c x.py -n "__fish_seen_subcommand_from run" -l llvm-skip-rebuild -d 'whether rebuilding llvm should be skipped, overriding `skip-rebuld` in config.toml' -r -f -a "{true ,false }"
+complete -c x.py -n "__fish_seen_subcommand_from run" -l color -d 'whether to use color in cargo and rustc output' -r -f -a "{always '',never '',auto ''}"
+complete -c x.py -n "__fish_seen_subcommand_from run" -l llvm-skip-rebuild -d 'whether rebuilding llvm should be skipped, overriding `skip-rebuld` in config.toml' -r -f -a "{true '',false ''}"
complete -c x.py -n "__fish_seen_subcommand_from run" -l rust-profile-generate -d 'generate PGO profile with rustc build' -r -F
complete -c x.py -n "__fish_seen_subcommand_from run" -l rust-profile-use -d 'use PGO profile for rustc build' -r -F
complete -c x.py -n "__fish_seen_subcommand_from run" -l llvm-profile-use -d 'use PGO profile for LLVM build' -r -F
@@ -426,6 +450,8 @@ complete -c x.py -n "__fish_seen_subcommand_from run" -l include-default-paths -
complete -c x.py -n "__fish_seen_subcommand_from run" -l dry-run -d 'dry run; don\'t build anything'
complete -c x.py -n "__fish_seen_subcommand_from run" -l json-output -d 'use message-format=json'
complete -c x.py -n "__fish_seen_subcommand_from run" -l llvm-profile-generate -d 'generate PGO profile with llvm built for rustc'
+complete -c x.py -n "__fish_seen_subcommand_from run" -l enable-bolt-settings -d 'Enable BOLT link flags'
+complete -c x.py -n "__fish_seen_subcommand_from run" -l skip-stage0-validation -d 'Skip stage0 compiler validation'
complete -c x.py -n "__fish_seen_subcommand_from run" -s h -l help -d 'Print help (see more with \'--help\')'
complete -c x.py -n "__fish_seen_subcommand_from setup" -l config -d 'TOML configuration file for build' -r -F
complete -c x.py -n "__fish_seen_subcommand_from setup" -l build-dir -d 'Build directory, overrides `build.build-dir` in `config.toml`' -r -f -a "(__fish_complete_directories)"
@@ -441,10 +467,10 @@ complete -c x.py -n "__fish_seen_subcommand_from setup" -l keep-stage -d 'stage(
complete -c x.py -n "__fish_seen_subcommand_from setup" -l keep-stage-std -d 'stage(s) of the standard library to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)' -r -f
complete -c x.py -n "__fish_seen_subcommand_from setup" -l src -d 'path to the root of the rust checkout' -r -f -a "(__fish_complete_directories)"
complete -c x.py -n "__fish_seen_subcommand_from setup" -s j -l jobs -d 'number of jobs to run in parallel' -r -f
-complete -c x.py -n "__fish_seen_subcommand_from setup" -l warnings -d 'if value is deny, will deny warnings if value is warn, will emit warnings otherwise, use the default configured behaviour' -r -f -a "{deny ,warn ,default }"
+complete -c x.py -n "__fish_seen_subcommand_from setup" -l warnings -d 'if value is deny, will deny warnings if value is warn, will emit warnings otherwise, use the default configured behaviour' -r -f -a "{deny '',warn '',default ''}"
complete -c x.py -n "__fish_seen_subcommand_from setup" -l error-format -d 'rustc error format' -r -f
-complete -c x.py -n "__fish_seen_subcommand_from setup" -l color -d 'whether to use color in cargo and rustc output' -r -f -a "{always ,never ,auto }"
-complete -c x.py -n "__fish_seen_subcommand_from setup" -l llvm-skip-rebuild -d 'whether rebuilding llvm should be skipped, overriding `skip-rebuld` in config.toml' -r -f -a "{true ,false }"
+complete -c x.py -n "__fish_seen_subcommand_from setup" -l color -d 'whether to use color in cargo and rustc output' -r -f -a "{always '',never '',auto ''}"
+complete -c x.py -n "__fish_seen_subcommand_from setup" -l llvm-skip-rebuild -d 'whether rebuilding llvm should be skipped, overriding `skip-rebuld` in config.toml' -r -f -a "{true '',false ''}"
complete -c x.py -n "__fish_seen_subcommand_from setup" -l rust-profile-generate -d 'generate PGO profile with rustc build' -r -F
complete -c x.py -n "__fish_seen_subcommand_from setup" -l rust-profile-use -d 'use PGO profile for rustc build' -r -F
complete -c x.py -n "__fish_seen_subcommand_from setup" -l llvm-profile-use -d 'use PGO profile for LLVM build' -r -F
@@ -456,6 +482,8 @@ complete -c x.py -n "__fish_seen_subcommand_from setup" -l include-default-paths
complete -c x.py -n "__fish_seen_subcommand_from setup" -l dry-run -d 'dry run; don\'t build anything'
complete -c x.py -n "__fish_seen_subcommand_from setup" -l json-output -d 'use message-format=json'
complete -c x.py -n "__fish_seen_subcommand_from setup" -l llvm-profile-generate -d 'generate PGO profile with llvm built for rustc'
+complete -c x.py -n "__fish_seen_subcommand_from setup" -l enable-bolt-settings -d 'Enable BOLT link flags'
+complete -c x.py -n "__fish_seen_subcommand_from setup" -l skip-stage0-validation -d 'Skip stage0 compiler validation'
complete -c x.py -n "__fish_seen_subcommand_from setup" -s h -l help -d 'Print help (see more with \'--help\')'
complete -c x.py -n "__fish_seen_subcommand_from suggest" -l config -d 'TOML configuration file for build' -r -F
complete -c x.py -n "__fish_seen_subcommand_from suggest" -l build-dir -d 'Build directory, overrides `build.build-dir` in `config.toml`' -r -f -a "(__fish_complete_directories)"
@@ -471,10 +499,10 @@ complete -c x.py -n "__fish_seen_subcommand_from suggest" -l keep-stage -d 'stag
complete -c x.py -n "__fish_seen_subcommand_from suggest" -l keep-stage-std -d 'stage(s) of the standard library to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)' -r -f
complete -c x.py -n "__fish_seen_subcommand_from suggest" -l src -d 'path to the root of the rust checkout' -r -f -a "(__fish_complete_directories)"
complete -c x.py -n "__fish_seen_subcommand_from suggest" -s j -l jobs -d 'number of jobs to run in parallel' -r -f
-complete -c x.py -n "__fish_seen_subcommand_from suggest" -l warnings -d 'if value is deny, will deny warnings if value is warn, will emit warnings otherwise, use the default configured behaviour' -r -f -a "{deny ,warn ,default }"
+complete -c x.py -n "__fish_seen_subcommand_from suggest" -l warnings -d 'if value is deny, will deny warnings if value is warn, will emit warnings otherwise, use the default configured behaviour' -r -f -a "{deny '',warn '',default ''}"
complete -c x.py -n "__fish_seen_subcommand_from suggest" -l error-format -d 'rustc error format' -r -f
-complete -c x.py -n "__fish_seen_subcommand_from suggest" -l color -d 'whether to use color in cargo and rustc output' -r -f -a "{always ,never ,auto }"
-complete -c x.py -n "__fish_seen_subcommand_from suggest" -l llvm-skip-rebuild -d 'whether rebuilding llvm should be skipped, overriding `skip-rebuld` in config.toml' -r -f -a "{true ,false }"
+complete -c x.py -n "__fish_seen_subcommand_from suggest" -l color -d 'whether to use color in cargo and rustc output' -r -f -a "{always '',never '',auto ''}"
+complete -c x.py -n "__fish_seen_subcommand_from suggest" -l llvm-skip-rebuild -d 'whether rebuilding llvm should be skipped, overriding `skip-rebuld` in config.toml' -r -f -a "{true '',false ''}"
complete -c x.py -n "__fish_seen_subcommand_from suggest" -l rust-profile-generate -d 'generate PGO profile with rustc build' -r -F
complete -c x.py -n "__fish_seen_subcommand_from suggest" -l rust-profile-use -d 'use PGO profile for rustc build' -r -F
complete -c x.py -n "__fish_seen_subcommand_from suggest" -l llvm-profile-use -d 'use PGO profile for LLVM build' -r -F
@@ -487,4 +515,6 @@ complete -c x.py -n "__fish_seen_subcommand_from suggest" -l include-default-pat
complete -c x.py -n "__fish_seen_subcommand_from suggest" -l dry-run -d 'dry run; don\'t build anything'
complete -c x.py -n "__fish_seen_subcommand_from suggest" -l json-output -d 'use message-format=json'
complete -c x.py -n "__fish_seen_subcommand_from suggest" -l llvm-profile-generate -d 'generate PGO profile with llvm built for rustc'
+complete -c x.py -n "__fish_seen_subcommand_from suggest" -l enable-bolt-settings -d 'Enable BOLT link flags'
+complete -c x.py -n "__fish_seen_subcommand_from suggest" -l skip-stage0-validation -d 'Skip stage0 compiler validation'
complete -c x.py -n "__fish_seen_subcommand_from suggest" -s h -l help -d 'Print help (see more with \'--help\')'
diff --git a/src/etc/completions/x.py.ps1 b/src/etc/completions/x.py.ps1
index cafb8eed1..2fed1be72 100644
--- a/src/etc/completions/x.py.ps1
+++ b/src/etc/completions/x.py.ps1
@@ -53,6 +53,8 @@ Register-ArgumentCompleter -Native -CommandName 'x.py' -ScriptBlock {
[CompletionResult]::new('--dry-run', 'dry-run', [CompletionResultType]::ParameterName, 'dry run; don''t build anything')
[CompletionResult]::new('--json-output', 'json-output', [CompletionResultType]::ParameterName, 'use message-format=json')
[CompletionResult]::new('--llvm-profile-generate', 'llvm-profile-generate', [CompletionResultType]::ParameterName, 'generate PGO profile with llvm built for rustc')
+ [CompletionResult]::new('--enable-bolt-settings', 'enable-bolt-settings', [CompletionResultType]::ParameterName, 'Enable BOLT link flags')
+ [CompletionResult]::new('--skip-stage0-validation', 'skip-stage0-validation', [CompletionResultType]::ParameterName, 'Skip stage0 compiler validation')
[CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Print help')
[CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Print help')
[CompletionResult]::new('build', 'build', [CompletionResultType]::ParameterValue, 'Compile either the compiler or libraries')
@@ -104,6 +106,8 @@ Register-ArgumentCompleter -Native -CommandName 'x.py' -ScriptBlock {
[CompletionResult]::new('--dry-run', 'dry-run', [CompletionResultType]::ParameterName, 'dry run; don''t build anything')
[CompletionResult]::new('--json-output', 'json-output', [CompletionResultType]::ParameterName, 'use message-format=json')
[CompletionResult]::new('--llvm-profile-generate', 'llvm-profile-generate', [CompletionResultType]::ParameterName, 'generate PGO profile with llvm built for rustc')
+ [CompletionResult]::new('--enable-bolt-settings', 'enable-bolt-settings', [CompletionResultType]::ParameterName, 'Enable BOLT link flags')
+ [CompletionResult]::new('--skip-stage0-validation', 'skip-stage0-validation', [CompletionResultType]::ParameterName, 'Skip stage0 compiler validation')
[CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Print help (see more with ''--help'')')
[CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Print help (see more with ''--help'')')
break
@@ -142,15 +146,17 @@ Register-ArgumentCompleter -Native -CommandName 'x.py' -ScriptBlock {
[CompletionResult]::new('--dry-run', 'dry-run', [CompletionResultType]::ParameterName, 'dry run; don''t build anything')
[CompletionResult]::new('--json-output', 'json-output', [CompletionResultType]::ParameterName, 'use message-format=json')
[CompletionResult]::new('--llvm-profile-generate', 'llvm-profile-generate', [CompletionResultType]::ParameterName, 'generate PGO profile with llvm built for rustc')
+ [CompletionResult]::new('--enable-bolt-settings', 'enable-bolt-settings', [CompletionResultType]::ParameterName, 'Enable BOLT link flags')
+ [CompletionResult]::new('--skip-stage0-validation', 'skip-stage0-validation', [CompletionResultType]::ParameterName, 'Skip stage0 compiler validation')
[CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Print help (see more with ''--help'')')
[CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Print help (see more with ''--help'')')
break
}
'x.py;clippy' {
- [CompletionResult]::new('-A', 'A', [CompletionResultType]::ParameterName, 'clippy lints to allow')
- [CompletionResult]::new('-D', 'D', [CompletionResultType]::ParameterName, 'clippy lints to deny')
- [CompletionResult]::new('-W', 'W', [CompletionResultType]::ParameterName, 'clippy lints to warn on')
- [CompletionResult]::new('-F', 'F', [CompletionResultType]::ParameterName, 'clippy lints to forbid')
+ [CompletionResult]::new('-A', 'A ', [CompletionResultType]::ParameterName, 'clippy lints to allow')
+ [CompletionResult]::new('-D', 'D ', [CompletionResultType]::ParameterName, 'clippy lints to deny')
+ [CompletionResult]::new('-W', 'W ', [CompletionResultType]::ParameterName, 'clippy lints to warn on')
+ [CompletionResult]::new('-F', 'F ', [CompletionResultType]::ParameterName, 'clippy lints to forbid')
[CompletionResult]::new('--config', 'config', [CompletionResultType]::ParameterName, 'TOML configuration file for build')
[CompletionResult]::new('--build-dir', 'build-dir', [CompletionResultType]::ParameterName, 'Build directory, overrides `build.build-dir` in `config.toml`')
[CompletionResult]::new('--build', 'build', [CompletionResultType]::ParameterName, 'build target of the stage0 compiler')
@@ -184,6 +190,8 @@ Register-ArgumentCompleter -Native -CommandName 'x.py' -ScriptBlock {
[CompletionResult]::new('--dry-run', 'dry-run', [CompletionResultType]::ParameterName, 'dry run; don''t build anything')
[CompletionResult]::new('--json-output', 'json-output', [CompletionResultType]::ParameterName, 'use message-format=json')
[CompletionResult]::new('--llvm-profile-generate', 'llvm-profile-generate', [CompletionResultType]::ParameterName, 'generate PGO profile with llvm built for rustc')
+ [CompletionResult]::new('--enable-bolt-settings', 'enable-bolt-settings', [CompletionResultType]::ParameterName, 'Enable BOLT link flags')
+ [CompletionResult]::new('--skip-stage0-validation', 'skip-stage0-validation', [CompletionResultType]::ParameterName, 'Skip stage0 compiler validation')
[CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Print help (see more with ''--help'')')
[CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Print help (see more with ''--help'')')
break
@@ -221,6 +229,8 @@ Register-ArgumentCompleter -Native -CommandName 'x.py' -ScriptBlock {
[CompletionResult]::new('--dry-run', 'dry-run', [CompletionResultType]::ParameterName, 'dry run; don''t build anything')
[CompletionResult]::new('--json-output', 'json-output', [CompletionResultType]::ParameterName, 'use message-format=json')
[CompletionResult]::new('--llvm-profile-generate', 'llvm-profile-generate', [CompletionResultType]::ParameterName, 'generate PGO profile with llvm built for rustc')
+ [CompletionResult]::new('--enable-bolt-settings', 'enable-bolt-settings', [CompletionResultType]::ParameterName, 'Enable BOLT link flags')
+ [CompletionResult]::new('--skip-stage0-validation', 'skip-stage0-validation', [CompletionResultType]::ParameterName, 'Skip stage0 compiler validation')
[CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Print help (see more with ''--help'')')
[CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Print help (see more with ''--help'')')
break
@@ -259,6 +269,8 @@ Register-ArgumentCompleter -Native -CommandName 'x.py' -ScriptBlock {
[CompletionResult]::new('--dry-run', 'dry-run', [CompletionResultType]::ParameterName, 'dry run; don''t build anything')
[CompletionResult]::new('--json-output', 'json-output', [CompletionResultType]::ParameterName, 'use message-format=json')
[CompletionResult]::new('--llvm-profile-generate', 'llvm-profile-generate', [CompletionResultType]::ParameterName, 'generate PGO profile with llvm built for rustc')
+ [CompletionResult]::new('--enable-bolt-settings', 'enable-bolt-settings', [CompletionResultType]::ParameterName, 'Enable BOLT link flags')
+ [CompletionResult]::new('--skip-stage0-validation', 'skip-stage0-validation', [CompletionResultType]::ParameterName, 'Skip stage0 compiler validation')
[CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Print help (see more with ''--help'')')
[CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Print help (see more with ''--help'')')
break
@@ -298,6 +310,8 @@ Register-ArgumentCompleter -Native -CommandName 'x.py' -ScriptBlock {
[CompletionResult]::new('--dry-run', 'dry-run', [CompletionResultType]::ParameterName, 'dry run; don''t build anything')
[CompletionResult]::new('--json-output', 'json-output', [CompletionResultType]::ParameterName, 'use message-format=json')
[CompletionResult]::new('--llvm-profile-generate', 'llvm-profile-generate', [CompletionResultType]::ParameterName, 'generate PGO profile with llvm built for rustc')
+ [CompletionResult]::new('--enable-bolt-settings', 'enable-bolt-settings', [CompletionResultType]::ParameterName, 'Enable BOLT link flags')
+ [CompletionResult]::new('--skip-stage0-validation', 'skip-stage0-validation', [CompletionResultType]::ParameterName, 'Skip stage0 compiler validation')
[CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Print help (see more with ''--help'')')
[CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Print help (see more with ''--help'')')
break
@@ -348,6 +362,8 @@ Register-ArgumentCompleter -Native -CommandName 'x.py' -ScriptBlock {
[CompletionResult]::new('--dry-run', 'dry-run', [CompletionResultType]::ParameterName, 'dry run; don''t build anything')
[CompletionResult]::new('--json-output', 'json-output', [CompletionResultType]::ParameterName, 'use message-format=json')
[CompletionResult]::new('--llvm-profile-generate', 'llvm-profile-generate', [CompletionResultType]::ParameterName, 'generate PGO profile with llvm built for rustc')
+ [CompletionResult]::new('--enable-bolt-settings', 'enable-bolt-settings', [CompletionResultType]::ParameterName, 'Enable BOLT link flags')
+ [CompletionResult]::new('--skip-stage0-validation', 'skip-stage0-validation', [CompletionResultType]::ParameterName, 'Skip stage0 compiler validation')
[CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Print help (see more with ''--help'')')
[CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Print help (see more with ''--help'')')
break
@@ -386,6 +402,8 @@ Register-ArgumentCompleter -Native -CommandName 'x.py' -ScriptBlock {
[CompletionResult]::new('--dry-run', 'dry-run', [CompletionResultType]::ParameterName, 'dry run; don''t build anything')
[CompletionResult]::new('--json-output', 'json-output', [CompletionResultType]::ParameterName, 'use message-format=json')
[CompletionResult]::new('--llvm-profile-generate', 'llvm-profile-generate', [CompletionResultType]::ParameterName, 'generate PGO profile with llvm built for rustc')
+ [CompletionResult]::new('--enable-bolt-settings', 'enable-bolt-settings', [CompletionResultType]::ParameterName, 'Enable BOLT link flags')
+ [CompletionResult]::new('--skip-stage0-validation', 'skip-stage0-validation', [CompletionResultType]::ParameterName, 'Skip stage0 compiler validation')
[CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Print help')
[CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Print help')
break
@@ -424,6 +442,8 @@ Register-ArgumentCompleter -Native -CommandName 'x.py' -ScriptBlock {
[CompletionResult]::new('--dry-run', 'dry-run', [CompletionResultType]::ParameterName, 'dry run; don''t build anything')
[CompletionResult]::new('--json-output', 'json-output', [CompletionResultType]::ParameterName, 'use message-format=json')
[CompletionResult]::new('--llvm-profile-generate', 'llvm-profile-generate', [CompletionResultType]::ParameterName, 'generate PGO profile with llvm built for rustc')
+ [CompletionResult]::new('--enable-bolt-settings', 'enable-bolt-settings', [CompletionResultType]::ParameterName, 'Enable BOLT link flags')
+ [CompletionResult]::new('--skip-stage0-validation', 'skip-stage0-validation', [CompletionResultType]::ParameterName, 'Skip stage0 compiler validation')
[CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Print help')
[CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Print help')
break
@@ -461,6 +481,8 @@ Register-ArgumentCompleter -Native -CommandName 'x.py' -ScriptBlock {
[CompletionResult]::new('--dry-run', 'dry-run', [CompletionResultType]::ParameterName, 'dry run; don''t build anything')
[CompletionResult]::new('--json-output', 'json-output', [CompletionResultType]::ParameterName, 'use message-format=json')
[CompletionResult]::new('--llvm-profile-generate', 'llvm-profile-generate', [CompletionResultType]::ParameterName, 'generate PGO profile with llvm built for rustc')
+ [CompletionResult]::new('--enable-bolt-settings', 'enable-bolt-settings', [CompletionResultType]::ParameterName, 'Enable BOLT link flags')
+ [CompletionResult]::new('--skip-stage0-validation', 'skip-stage0-validation', [CompletionResultType]::ParameterName, 'Skip stage0 compiler validation')
[CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Print help')
[CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Print help')
break
@@ -498,6 +520,8 @@ Register-ArgumentCompleter -Native -CommandName 'x.py' -ScriptBlock {
[CompletionResult]::new('--dry-run', 'dry-run', [CompletionResultType]::ParameterName, 'dry run; don''t build anything')
[CompletionResult]::new('--json-output', 'json-output', [CompletionResultType]::ParameterName, 'use message-format=json')
[CompletionResult]::new('--llvm-profile-generate', 'llvm-profile-generate', [CompletionResultType]::ParameterName, 'generate PGO profile with llvm built for rustc')
+ [CompletionResult]::new('--enable-bolt-settings', 'enable-bolt-settings', [CompletionResultType]::ParameterName, 'Enable BOLT link flags')
+ [CompletionResult]::new('--skip-stage0-validation', 'skip-stage0-validation', [CompletionResultType]::ParameterName, 'Skip stage0 compiler validation')
[CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Print help')
[CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Print help')
break
@@ -536,6 +560,8 @@ Register-ArgumentCompleter -Native -CommandName 'x.py' -ScriptBlock {
[CompletionResult]::new('--dry-run', 'dry-run', [CompletionResultType]::ParameterName, 'dry run; don''t build anything')
[CompletionResult]::new('--json-output', 'json-output', [CompletionResultType]::ParameterName, 'use message-format=json')
[CompletionResult]::new('--llvm-profile-generate', 'llvm-profile-generate', [CompletionResultType]::ParameterName, 'generate PGO profile with llvm built for rustc')
+ [CompletionResult]::new('--enable-bolt-settings', 'enable-bolt-settings', [CompletionResultType]::ParameterName, 'Enable BOLT link flags')
+ [CompletionResult]::new('--skip-stage0-validation', 'skip-stage0-validation', [CompletionResultType]::ParameterName, 'Skip stage0 compiler validation')
[CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Print help (see more with ''--help'')')
[CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Print help (see more with ''--help'')')
break
@@ -573,6 +599,8 @@ Register-ArgumentCompleter -Native -CommandName 'x.py' -ScriptBlock {
[CompletionResult]::new('--dry-run', 'dry-run', [CompletionResultType]::ParameterName, 'dry run; don''t build anything')
[CompletionResult]::new('--json-output', 'json-output', [CompletionResultType]::ParameterName, 'use message-format=json')
[CompletionResult]::new('--llvm-profile-generate', 'llvm-profile-generate', [CompletionResultType]::ParameterName, 'generate PGO profile with llvm built for rustc')
+ [CompletionResult]::new('--enable-bolt-settings', 'enable-bolt-settings', [CompletionResultType]::ParameterName, 'Enable BOLT link flags')
+ [CompletionResult]::new('--skip-stage0-validation', 'skip-stage0-validation', [CompletionResultType]::ParameterName, 'Skip stage0 compiler validation')
[CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Print help (see more with ''--help'')')
[CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Print help (see more with ''--help'')')
break
@@ -611,6 +639,8 @@ Register-ArgumentCompleter -Native -CommandName 'x.py' -ScriptBlock {
[CompletionResult]::new('--dry-run', 'dry-run', [CompletionResultType]::ParameterName, 'dry run; don''t build anything')
[CompletionResult]::new('--json-output', 'json-output', [CompletionResultType]::ParameterName, 'use message-format=json')
[CompletionResult]::new('--llvm-profile-generate', 'llvm-profile-generate', [CompletionResultType]::ParameterName, 'generate PGO profile with llvm built for rustc')
+ [CompletionResult]::new('--enable-bolt-settings', 'enable-bolt-settings', [CompletionResultType]::ParameterName, 'Enable BOLT link flags')
+ [CompletionResult]::new('--skip-stage0-validation', 'skip-stage0-validation', [CompletionResultType]::ParameterName, 'Skip stage0 compiler validation')
[CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Print help (see more with ''--help'')')
[CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Print help (see more with ''--help'')')
break
diff --git a/src/etc/completions/x.py.sh b/src/etc/completions/x.py.sh
index 3c57e71bd..f22d7e3e1 100644
--- a/src/etc/completions/x.py.sh
+++ b/src/etc/completions/x.py.sh
@@ -61,7 +61,7 @@ _x.py() {
case "${cmd}" in
x.py)
- opts="-v -i -j -h --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --stage --keep-stage --keep-stage-std --src --jobs --warnings --error-format --json-output --color --llvm-skip-rebuild --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --reproducible-artifact --set --help [PATHS]... [ARGS]... build check clippy fix fmt doc test bench clean dist install run setup suggest"
+ opts="-v -i -j -h --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --stage --keep-stage --keep-stage-std --src --jobs --warnings --error-format --json-output --color --llvm-skip-rebuild --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --help [PATHS]... [ARGS]... build check clippy fix fmt doc test bench clean dist install run setup suggest"
if [[ ${cur} == -* || ${COMP_CWORD} -eq 1 ]] ; then
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
@@ -171,7 +171,7 @@ _x.py() {
return 0
;;
x.py__bench)
- opts="-v -i -j -h --test-args --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --stage --keep-stage --keep-stage-std --src --jobs --warnings --error-format --json-output --color --llvm-skip-rebuild --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --reproducible-artifact --set --help [PATHS]... [ARGS]..."
+ opts="-v -i -j -h --test-args --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --stage --keep-stage --keep-stage-std --src --jobs --warnings --error-format --json-output --color --llvm-skip-rebuild --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --help [PATHS]... [ARGS]..."
if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
@@ -285,7 +285,7 @@ _x.py() {
return 0
;;
x.py__build)
- opts="-v -i -j -h --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --stage --keep-stage --keep-stage-std --src --jobs --warnings --error-format --json-output --color --llvm-skip-rebuild --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --reproducible-artifact --set --help [PATHS]... [ARGS]..."
+ opts="-v -i -j -h --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --stage --keep-stage --keep-stage-std --src --jobs --warnings --error-format --json-output --color --llvm-skip-rebuild --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --help [PATHS]... [ARGS]..."
if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
@@ -395,7 +395,7 @@ _x.py() {
return 0
;;
x.py__check)
- opts="-v -i -j -h --all-targets --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --stage --keep-stage --keep-stage-std --src --jobs --warnings --error-format --json-output --color --llvm-skip-rebuild --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --reproducible-artifact --set --help [PATHS]... [ARGS]..."
+ opts="-v -i -j -h --all-targets --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --stage --keep-stage --keep-stage-std --src --jobs --warnings --error-format --json-output --color --llvm-skip-rebuild --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --help [PATHS]... [ARGS]..."
if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
@@ -505,7 +505,7 @@ _x.py() {
return 0
;;
x.py__clean)
- opts="-v -i -j -h --all --stage --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --keep-stage --keep-stage-std --src --jobs --warnings --error-format --json-output --color --llvm-skip-rebuild --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --reproducible-artifact --set --help [PATHS]... [ARGS]..."
+ opts="-v -i -j -h --all --stage --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --keep-stage --keep-stage-std --src --jobs --warnings --error-format --json-output --color --llvm-skip-rebuild --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --help [PATHS]... [ARGS]..."
if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
@@ -615,7 +615,7 @@ _x.py() {
return 0
;;
x.py__clippy)
- opts="-A -D -W -F -v -i -j -h --fix --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --stage --keep-stage --keep-stage-std --src --jobs --warnings --error-format --json-output --color --llvm-skip-rebuild --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --reproducible-artifact --set --help [PATHS]... [ARGS]..."
+ opts="-A -D -W -F -v -i -j -h --fix --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --stage --keep-stage --keep-stage-std --src --jobs --warnings --error-format --json-output --color --llvm-skip-rebuild --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --help [PATHS]... [ARGS]..."
if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
@@ -741,7 +741,7 @@ _x.py() {
return 0
;;
x.py__dist)
- opts="-v -i -j -h --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --stage --keep-stage --keep-stage-std --src --jobs --warnings --error-format --json-output --color --llvm-skip-rebuild --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --reproducible-artifact --set --help [PATHS]... [ARGS]..."
+ opts="-v -i -j -h --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --stage --keep-stage --keep-stage-std --src --jobs --warnings --error-format --json-output --color --llvm-skip-rebuild --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --help [PATHS]... [ARGS]..."
if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
@@ -851,7 +851,7 @@ _x.py() {
return 0
;;
x.py__doc)
- opts="-v -i -j -h --open --json --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --stage --keep-stage --keep-stage-std --src --jobs --warnings --error-format --json-output --color --llvm-skip-rebuild --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --reproducible-artifact --set --help [PATHS]... [ARGS]..."
+ opts="-v -i -j -h --open --json --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --stage --keep-stage --keep-stage-std --src --jobs --warnings --error-format --json-output --color --llvm-skip-rebuild --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --help [PATHS]... [ARGS]..."
if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
@@ -961,7 +961,7 @@ _x.py() {
return 0
;;
x.py__fix)
- opts="-v -i -j -h --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --stage --keep-stage --keep-stage-std --src --jobs --warnings --error-format --json-output --color --llvm-skip-rebuild --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --reproducible-artifact --set --help [PATHS]... [ARGS]..."
+ opts="-v -i -j -h --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --stage --keep-stage --keep-stage-std --src --jobs --warnings --error-format --json-output --color --llvm-skip-rebuild --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --help [PATHS]... [ARGS]..."
if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
@@ -1071,7 +1071,7 @@ _x.py() {
return 0
;;
x.py__fmt)
- opts="-v -i -j -h --check --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --stage --keep-stage --keep-stage-std --src --jobs --warnings --error-format --json-output --color --llvm-skip-rebuild --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --reproducible-artifact --set --help [PATHS]... [ARGS]..."
+ opts="-v -i -j -h --check --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --stage --keep-stage --keep-stage-std --src --jobs --warnings --error-format --json-output --color --llvm-skip-rebuild --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --help [PATHS]... [ARGS]..."
if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
@@ -1181,7 +1181,7 @@ _x.py() {
return 0
;;
x.py__install)
- opts="-v -i -j -h --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --stage --keep-stage --keep-stage-std --src --jobs --warnings --error-format --json-output --color --llvm-skip-rebuild --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --reproducible-artifact --set --help [PATHS]... [ARGS]..."
+ opts="-v -i -j -h --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --stage --keep-stage --keep-stage-std --src --jobs --warnings --error-format --json-output --color --llvm-skip-rebuild --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --help [PATHS]... [ARGS]..."
if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
@@ -1291,7 +1291,7 @@ _x.py() {
return 0
;;
x.py__run)
- opts="-v -i -j -h --args --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --stage --keep-stage --keep-stage-std --src --jobs --warnings --error-format --json-output --color --llvm-skip-rebuild --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --reproducible-artifact --set --help [PATHS]... [ARGS]..."
+ opts="-v -i -j -h --args --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --stage --keep-stage --keep-stage-std --src --jobs --warnings --error-format --json-output --color --llvm-skip-rebuild --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --help [PATHS]... [ARGS]..."
if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
@@ -1405,7 +1405,7 @@ _x.py() {
return 0
;;
x.py__setup)
- opts="-v -i -j -h --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --stage --keep-stage --keep-stage-std --src --jobs --warnings --error-format --json-output --color --llvm-skip-rebuild --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --reproducible-artifact --set --help [<PROFILE>|hook|vscode|link] [PATHS]... [ARGS]..."
+ opts="-v -i -j -h --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --stage --keep-stage --keep-stage-std --src --jobs --warnings --error-format --json-output --color --llvm-skip-rebuild --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --help [<PROFILE>|hook|vscode|link] [PATHS]... [ARGS]..."
if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
@@ -1515,7 +1515,7 @@ _x.py() {
return 0
;;
x.py__suggest)
- opts="-v -i -j -h --run --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --stage --keep-stage --keep-stage-std --src --jobs --warnings --error-format --json-output --color --llvm-skip-rebuild --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --reproducible-artifact --set --help [PATHS]... [ARGS]..."
+ opts="-v -i -j -h --run --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --stage --keep-stage --keep-stage-std --src --jobs --warnings --error-format --json-output --color --llvm-skip-rebuild --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --help [PATHS]... [ARGS]..."
if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
@@ -1625,7 +1625,7 @@ _x.py() {
return 0
;;
x.py__test)
- opts="-v -i -j -h --no-fail-fast --skip --test-args --rustc-args --no-doc --doc --bless --extra-checks --force-rerun --only-modified --compare-mode --pass --run --rustfix-coverage --verbose --incremental --config --build-dir --build --host --target --exclude --include-default-paths --rustc-error-format --on-fail --dry-run --stage --keep-stage --keep-stage-std --src --jobs --warnings --error-format --json-output --color --llvm-skip-rebuild --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --reproducible-artifact --set --help [PATHS]... [ARGS]..."
+ opts="-v -i -j -h --no-fail-fast --skip --test-args --rustc-args --no-doc --doc --bless --extra-checks --force-rerun --only-modified --compare-mode --pass --run --rustfix-coverage --verbose --incremental --config --build-dir --build --host --target --exclude --include-default-paths --rustc-error-format --on-fail --dry-run --stage --keep-stage --keep-stage-std --src --jobs --warnings --error-format --json-output --color --llvm-skip-rebuild --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --help [PATHS]... [ARGS]..."
if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
@@ -1761,4 +1761,4 @@ _x.py() {
esac
}
-complete -F _x.py -o bashdefault -o default x.py
+complete -F _x.py -o nosort -o bashdefault -o default x.py
diff --git a/src/etc/completions/x.py.zsh b/src/etc/completions/x.py.zsh
new file mode 100644
index 000000000..1e5a7b5aa
--- /dev/null
+++ b/src/etc/completions/x.py.zsh
@@ -0,0 +1,766 @@
+#compdef x.py
+
+autoload -U is-at-least
+
+_x.py() {
+ typeset -A opt_args
+ typeset -a _arguments_options
+ local ret=1
+
+ if is-at-least 5.2; then
+ _arguments_options=(-s -S -C)
+ else
+ _arguments_options=(-s -C)
+ fi
+
+ local context curcontext="$curcontext" state line
+ _arguments "${_arguments_options[@]}" \
+'--config=[TOML configuration file for build]:FILE:_files' \
+'--build-dir=[Build directory, overrides \`build.build-dir\` in \`config.toml\`]:DIR:_files -/' \
+'--build=[build target of the stage0 compiler]:BUILD:( )' \
+'--host=[host targets to build]:HOST:( )' \
+'--target=[target targets to build]:TARGET:( )' \
+'*--exclude=[build paths to exclude]:PATH:_files' \
+'*--skip=[build paths to skip]:PATH:_files' \
+'--rustc-error-format=[]:RUSTC_ERROR_FORMAT:( )' \
+'--on-fail=[command to run on failure]:CMD:_cmdstring' \
+'--stage=[stage to build (indicates compiler to use/test, e.g., stage 0 uses the bootstrap compiler, stage 1 the stage 0 rustc artifacts, etc.)]:N:( )' \
+'*--keep-stage=[stage(s) to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)]:N:( )' \
+'*--keep-stage-std=[stage(s) of the standard library to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)]:N:( )' \
+'--src=[path to the root of the rust checkout]:DIR:_files -/' \
+'-j+[number of jobs to run in parallel]:JOBS:( )' \
+'--jobs=[number of jobs to run in parallel]:JOBS:( )' \
+'--warnings=[if value is deny, will deny warnings if value is warn, will emit warnings otherwise, use the default configured behaviour]:deny|warn:(deny warn default)' \
+'--error-format=[rustc error format]:FORMAT:( )' \
+'--color=[whether to use color in cargo and rustc output]:STYLE:(always never auto)' \
+'--llvm-skip-rebuild=[whether rebuilding llvm should be skipped, overriding \`skip-rebuld\` in config.toml]:VALUE:(true false)' \
+'--rust-profile-generate=[generate PGO profile with rustc build]:PROFILE:_files' \
+'--rust-profile-use=[use PGO profile for rustc build]:PROFILE:_files' \
+'--llvm-profile-use=[use PGO profile for LLVM build]:PROFILE:_files' \
+'*--reproducible-artifact=[Additional reproducible artifacts that should be added to the reproducible artifacts archive]:REPRODUCIBLE_ARTIFACT: ' \
+'*--set=[override options in config.toml]:section.option=value:( )' \
+'*-v[use verbose output (-vv for very verbose)]' \
+'*--verbose[use verbose output (-vv for very verbose)]' \
+'-i[use incremental compilation]' \
+'--incremental[use incremental compilation]' \
+'--include-default-paths[include default paths in addition to the provided ones]' \
+'--dry-run[dry run; don'\''t build anything]' \
+'--json-output[use message-format=json]' \
+'--llvm-profile-generate[generate PGO profile with llvm built for rustc]' \
+'--enable-bolt-settings[Enable BOLT link flags]' \
+'--skip-stage0-validation[Skip stage0 compiler validation]' \
+'-h[Print help]' \
+'--help[Print help]' \
+'::paths -- paths for the subcommand:_files' \
+'::free_args -- arguments passed to subcommands:' \
+":: :_x.py_commands" \
+"*::: :->bootstrap" \
+&& ret=0
+ case $state in
+ (bootstrap)
+ words=($line[3] "${words[@]}")
+ (( CURRENT += 1 ))
+ curcontext="${curcontext%:*:*}:x.py-command-$line[3]:"
+ case $line[3] in
+ (build)
+_arguments "${_arguments_options[@]}" \
+'--config=[TOML configuration file for build]:FILE:_files' \
+'--build-dir=[Build directory, overrides \`build.build-dir\` in \`config.toml\`]:DIR:_files -/' \
+'--build=[build target of the stage0 compiler]:BUILD:( )' \
+'--host=[host targets to build]:HOST:( )' \
+'--target=[target targets to build]:TARGET:( )' \
+'*--exclude=[build paths to exclude]:PATH:_files' \
+'*--skip=[build paths to skip]:PATH:_files' \
+'--rustc-error-format=[]:RUSTC_ERROR_FORMAT:( )' \
+'--on-fail=[command to run on failure]:CMD:_cmdstring' \
+'--stage=[stage to build (indicates compiler to use/test, e.g., stage 0 uses the bootstrap compiler, stage 1 the stage 0 rustc artifacts, etc.)]:N:( )' \
+'*--keep-stage=[stage(s) to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)]:N:( )' \
+'*--keep-stage-std=[stage(s) of the standard library to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)]:N:( )' \
+'--src=[path to the root of the rust checkout]:DIR:_files -/' \
+'-j+[number of jobs to run in parallel]:JOBS:( )' \
+'--jobs=[number of jobs to run in parallel]:JOBS:( )' \
+'--warnings=[if value is deny, will deny warnings if value is warn, will emit warnings otherwise, use the default configured behaviour]:deny|warn:(deny warn default)' \
+'--error-format=[rustc error format]:FORMAT:( )' \
+'--color=[whether to use color in cargo and rustc output]:STYLE:(always never auto)' \
+'--llvm-skip-rebuild=[whether rebuilding llvm should be skipped, overriding \`skip-rebuld\` in config.toml]:VALUE:(true false)' \
+'--rust-profile-generate=[generate PGO profile with rustc build]:PROFILE:_files' \
+'--rust-profile-use=[use PGO profile for rustc build]:PROFILE:_files' \
+'--llvm-profile-use=[use PGO profile for LLVM build]:PROFILE:_files' \
+'*--reproducible-artifact=[Additional reproducible artifacts that should be added to the reproducible artifacts archive]:REPRODUCIBLE_ARTIFACT: ' \
+'*--set=[override options in config.toml]:section.option=value:( )' \
+'*-v[use verbose output (-vv for very verbose)]' \
+'*--verbose[use verbose output (-vv for very verbose)]' \
+'-i[use incremental compilation]' \
+'--incremental[use incremental compilation]' \
+'--include-default-paths[include default paths in addition to the provided ones]' \
+'--dry-run[dry run; don'\''t build anything]' \
+'--json-output[use message-format=json]' \
+'--llvm-profile-generate[generate PGO profile with llvm built for rustc]' \
+'--enable-bolt-settings[Enable BOLT link flags]' \
+'--skip-stage0-validation[Skip stage0 compiler validation]' \
+'-h[Print help (see more with '\''--help'\'')]' \
+'--help[Print help (see more with '\''--help'\'')]' \
+'*::paths -- paths for the subcommand:_files' \
+&& ret=0
+;;
+(check)
+_arguments "${_arguments_options[@]}" \
+'--config=[TOML configuration file for build]:FILE:_files' \
+'--build-dir=[Build directory, overrides \`build.build-dir\` in \`config.toml\`]:DIR:_files -/' \
+'--build=[build target of the stage0 compiler]:BUILD:( )' \
+'--host=[host targets to build]:HOST:( )' \
+'--target=[target targets to build]:TARGET:( )' \
+'*--exclude=[build paths to exclude]:PATH:_files' \
+'*--skip=[build paths to skip]:PATH:_files' \
+'--rustc-error-format=[]:RUSTC_ERROR_FORMAT:( )' \
+'--on-fail=[command to run on failure]:CMD:_cmdstring' \
+'--stage=[stage to build (indicates compiler to use/test, e.g., stage 0 uses the bootstrap compiler, stage 1 the stage 0 rustc artifacts, etc.)]:N:( )' \
+'*--keep-stage=[stage(s) to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)]:N:( )' \
+'*--keep-stage-std=[stage(s) of the standard library to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)]:N:( )' \
+'--src=[path to the root of the rust checkout]:DIR:_files -/' \
+'-j+[number of jobs to run in parallel]:JOBS:( )' \
+'--jobs=[number of jobs to run in parallel]:JOBS:( )' \
+'--warnings=[if value is deny, will deny warnings if value is warn, will emit warnings otherwise, use the default configured behaviour]:deny|warn:(deny warn default)' \
+'--error-format=[rustc error format]:FORMAT:( )' \
+'--color=[whether to use color in cargo and rustc output]:STYLE:(always never auto)' \
+'--llvm-skip-rebuild=[whether rebuilding llvm should be skipped, overriding \`skip-rebuld\` in config.toml]:VALUE:(true false)' \
+'--rust-profile-generate=[generate PGO profile with rustc build]:PROFILE:_files' \
+'--rust-profile-use=[use PGO profile for rustc build]:PROFILE:_files' \
+'--llvm-profile-use=[use PGO profile for LLVM build]:PROFILE:_files' \
+'*--reproducible-artifact=[Additional reproducible artifacts that should be added to the reproducible artifacts archive]:REPRODUCIBLE_ARTIFACT: ' \
+'*--set=[override options in config.toml]:section.option=value:( )' \
+'--all-targets[Check all targets]' \
+'*-v[use verbose output (-vv for very verbose)]' \
+'*--verbose[use verbose output (-vv for very verbose)]' \
+'-i[use incremental compilation]' \
+'--incremental[use incremental compilation]' \
+'--include-default-paths[include default paths in addition to the provided ones]' \
+'--dry-run[dry run; don'\''t build anything]' \
+'--json-output[use message-format=json]' \
+'--llvm-profile-generate[generate PGO profile with llvm built for rustc]' \
+'--enable-bolt-settings[Enable BOLT link flags]' \
+'--skip-stage0-validation[Skip stage0 compiler validation]' \
+'-h[Print help (see more with '\''--help'\'')]' \
+'--help[Print help (see more with '\''--help'\'')]' \
+'*::paths -- paths for the subcommand:_files' \
+&& ret=0
+;;
+(clippy)
+_arguments "${_arguments_options[@]}" \
+'*-A+[clippy lints to allow]:LINT: ' \
+'*-D+[clippy lints to deny]:LINT: ' \
+'*-W+[clippy lints to warn on]:LINT: ' \
+'*-F+[clippy lints to forbid]:LINT: ' \
+'--config=[TOML configuration file for build]:FILE:_files' \
+'--build-dir=[Build directory, overrides \`build.build-dir\` in \`config.toml\`]:DIR:_files -/' \
+'--build=[build target of the stage0 compiler]:BUILD:( )' \
+'--host=[host targets to build]:HOST:( )' \
+'--target=[target targets to build]:TARGET:( )' \
+'*--exclude=[build paths to exclude]:PATH:_files' \
+'*--skip=[build paths to skip]:PATH:_files' \
+'--rustc-error-format=[]:RUSTC_ERROR_FORMAT:( )' \
+'--on-fail=[command to run on failure]:CMD:_cmdstring' \
+'--stage=[stage to build (indicates compiler to use/test, e.g., stage 0 uses the bootstrap compiler, stage 1 the stage 0 rustc artifacts, etc.)]:N:( )' \
+'*--keep-stage=[stage(s) to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)]:N:( )' \
+'*--keep-stage-std=[stage(s) of the standard library to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)]:N:( )' \
+'--src=[path to the root of the rust checkout]:DIR:_files -/' \
+'-j+[number of jobs to run in parallel]:JOBS:( )' \
+'--jobs=[number of jobs to run in parallel]:JOBS:( )' \
+'--warnings=[if value is deny, will deny warnings if value is warn, will emit warnings otherwise, use the default configured behaviour]:deny|warn:(deny warn default)' \
+'--error-format=[rustc error format]:FORMAT:( )' \
+'--color=[whether to use color in cargo and rustc output]:STYLE:(always never auto)' \
+'--llvm-skip-rebuild=[whether rebuilding llvm should be skipped, overriding \`skip-rebuld\` in config.toml]:VALUE:(true false)' \
+'--rust-profile-generate=[generate PGO profile with rustc build]:PROFILE:_files' \
+'--rust-profile-use=[use PGO profile for rustc build]:PROFILE:_files' \
+'--llvm-profile-use=[use PGO profile for LLVM build]:PROFILE:_files' \
+'*--reproducible-artifact=[Additional reproducible artifacts that should be added to the reproducible artifacts archive]:REPRODUCIBLE_ARTIFACT: ' \
+'*--set=[override options in config.toml]:section.option=value:( )' \
+'--fix[]' \
+'*-v[use verbose output (-vv for very verbose)]' \
+'*--verbose[use verbose output (-vv for very verbose)]' \
+'-i[use incremental compilation]' \
+'--incremental[use incremental compilation]' \
+'--include-default-paths[include default paths in addition to the provided ones]' \
+'--dry-run[dry run; don'\''t build anything]' \
+'--json-output[use message-format=json]' \
+'--llvm-profile-generate[generate PGO profile with llvm built for rustc]' \
+'--enable-bolt-settings[Enable BOLT link flags]' \
+'--skip-stage0-validation[Skip stage0 compiler validation]' \
+'-h[Print help (see more with '\''--help'\'')]' \
+'--help[Print help (see more with '\''--help'\'')]' \
+'*::paths -- paths for the subcommand:_files' \
+&& ret=0
+;;
+(fix)
+_arguments "${_arguments_options[@]}" \
+'--config=[TOML configuration file for build]:FILE:_files' \
+'--build-dir=[Build directory, overrides \`build.build-dir\` in \`config.toml\`]:DIR:_files -/' \
+'--build=[build target of the stage0 compiler]:BUILD:( )' \
+'--host=[host targets to build]:HOST:( )' \
+'--target=[target targets to build]:TARGET:( )' \
+'*--exclude=[build paths to exclude]:PATH:_files' \
+'*--skip=[build paths to skip]:PATH:_files' \
+'--rustc-error-format=[]:RUSTC_ERROR_FORMAT:( )' \
+'--on-fail=[command to run on failure]:CMD:_cmdstring' \
+'--stage=[stage to build (indicates compiler to use/test, e.g., stage 0 uses the bootstrap compiler, stage 1 the stage 0 rustc artifacts, etc.)]:N:( )' \
+'*--keep-stage=[stage(s) to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)]:N:( )' \
+'*--keep-stage-std=[stage(s) of the standard library to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)]:N:( )' \
+'--src=[path to the root of the rust checkout]:DIR:_files -/' \
+'-j+[number of jobs to run in parallel]:JOBS:( )' \
+'--jobs=[number of jobs to run in parallel]:JOBS:( )' \
+'--warnings=[if value is deny, will deny warnings if value is warn, will emit warnings otherwise, use the default configured behaviour]:deny|warn:(deny warn default)' \
+'--error-format=[rustc error format]:FORMAT:( )' \
+'--color=[whether to use color in cargo and rustc output]:STYLE:(always never auto)' \
+'--llvm-skip-rebuild=[whether rebuilding llvm should be skipped, overriding \`skip-rebuld\` in config.toml]:VALUE:(true false)' \
+'--rust-profile-generate=[generate PGO profile with rustc build]:PROFILE:_files' \
+'--rust-profile-use=[use PGO profile for rustc build]:PROFILE:_files' \
+'--llvm-profile-use=[use PGO profile for LLVM build]:PROFILE:_files' \
+'*--reproducible-artifact=[Additional reproducible artifacts that should be added to the reproducible artifacts archive]:REPRODUCIBLE_ARTIFACT: ' \
+'*--set=[override options in config.toml]:section.option=value:( )' \
+'*-v[use verbose output (-vv for very verbose)]' \
+'*--verbose[use verbose output (-vv for very verbose)]' \
+'-i[use incremental compilation]' \
+'--incremental[use incremental compilation]' \
+'--include-default-paths[include default paths in addition to the provided ones]' \
+'--dry-run[dry run; don'\''t build anything]' \
+'--json-output[use message-format=json]' \
+'--llvm-profile-generate[generate PGO profile with llvm built for rustc]' \
+'--enable-bolt-settings[Enable BOLT link flags]' \
+'--skip-stage0-validation[Skip stage0 compiler validation]' \
+'-h[Print help (see more with '\''--help'\'')]' \
+'--help[Print help (see more with '\''--help'\'')]' \
+'*::paths -- paths for the subcommand:_files' \
+&& ret=0
+;;
+(fmt)
+_arguments "${_arguments_options[@]}" \
+'--config=[TOML configuration file for build]:FILE:_files' \
+'--build-dir=[Build directory, overrides \`build.build-dir\` in \`config.toml\`]:DIR:_files -/' \
+'--build=[build target of the stage0 compiler]:BUILD:( )' \
+'--host=[host targets to build]:HOST:( )' \
+'--target=[target targets to build]:TARGET:( )' \
+'*--exclude=[build paths to exclude]:PATH:_files' \
+'*--skip=[build paths to skip]:PATH:_files' \
+'--rustc-error-format=[]:RUSTC_ERROR_FORMAT:( )' \
+'--on-fail=[command to run on failure]:CMD:_cmdstring' \
+'--stage=[stage to build (indicates compiler to use/test, e.g., stage 0 uses the bootstrap compiler, stage 1 the stage 0 rustc artifacts, etc.)]:N:( )' \
+'*--keep-stage=[stage(s) to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)]:N:( )' \
+'*--keep-stage-std=[stage(s) of the standard library to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)]:N:( )' \
+'--src=[path to the root of the rust checkout]:DIR:_files -/' \
+'-j+[number of jobs to run in parallel]:JOBS:( )' \
+'--jobs=[number of jobs to run in parallel]:JOBS:( )' \
+'--warnings=[if value is deny, will deny warnings if value is warn, will emit warnings otherwise, use the default configured behaviour]:deny|warn:(deny warn default)' \
+'--error-format=[rustc error format]:FORMAT:( )' \
+'--color=[whether to use color in cargo and rustc output]:STYLE:(always never auto)' \
+'--llvm-skip-rebuild=[whether rebuilding llvm should be skipped, overriding \`skip-rebuld\` in config.toml]:VALUE:(true false)' \
+'--rust-profile-generate=[generate PGO profile with rustc build]:PROFILE:_files' \
+'--rust-profile-use=[use PGO profile for rustc build]:PROFILE:_files' \
+'--llvm-profile-use=[use PGO profile for LLVM build]:PROFILE:_files' \
+'*--reproducible-artifact=[Additional reproducible artifacts that should be added to the reproducible artifacts archive]:REPRODUCIBLE_ARTIFACT: ' \
+'*--set=[override options in config.toml]:section.option=value:( )' \
+'--check[check formatting instead of applying]' \
+'*-v[use verbose output (-vv for very verbose)]' \
+'*--verbose[use verbose output (-vv for very verbose)]' \
+'-i[use incremental compilation]' \
+'--incremental[use incremental compilation]' \
+'--include-default-paths[include default paths in addition to the provided ones]' \
+'--dry-run[dry run; don'\''t build anything]' \
+'--json-output[use message-format=json]' \
+'--llvm-profile-generate[generate PGO profile with llvm built for rustc]' \
+'--enable-bolt-settings[Enable BOLT link flags]' \
+'--skip-stage0-validation[Skip stage0 compiler validation]' \
+'-h[Print help (see more with '\''--help'\'')]' \
+'--help[Print help (see more with '\''--help'\'')]' \
+'*::paths -- paths for the subcommand:_files' \
+&& ret=0
+;;
+(doc)
+_arguments "${_arguments_options[@]}" \
+'--config=[TOML configuration file for build]:FILE:_files' \
+'--build-dir=[Build directory, overrides \`build.build-dir\` in \`config.toml\`]:DIR:_files -/' \
+'--build=[build target of the stage0 compiler]:BUILD:( )' \
+'--host=[host targets to build]:HOST:( )' \
+'--target=[target targets to build]:TARGET:( )' \
+'*--exclude=[build paths to exclude]:PATH:_files' \
+'*--skip=[build paths to skip]:PATH:_files' \
+'--rustc-error-format=[]:RUSTC_ERROR_FORMAT:( )' \
+'--on-fail=[command to run on failure]:CMD:_cmdstring' \
+'--stage=[stage to build (indicates compiler to use/test, e.g., stage 0 uses the bootstrap compiler, stage 1 the stage 0 rustc artifacts, etc.)]:N:( )' \
+'*--keep-stage=[stage(s) to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)]:N:( )' \
+'*--keep-stage-std=[stage(s) of the standard library to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)]:N:( )' \
+'--src=[path to the root of the rust checkout]:DIR:_files -/' \
+'-j+[number of jobs to run in parallel]:JOBS:( )' \
+'--jobs=[number of jobs to run in parallel]:JOBS:( )' \
+'--warnings=[if value is deny, will deny warnings if value is warn, will emit warnings otherwise, use the default configured behaviour]:deny|warn:(deny warn default)' \
+'--error-format=[rustc error format]:FORMAT:( )' \
+'--color=[whether to use color in cargo and rustc output]:STYLE:(always never auto)' \
+'--llvm-skip-rebuild=[whether rebuilding llvm should be skipped, overriding \`skip-rebuld\` in config.toml]:VALUE:(true false)' \
+'--rust-profile-generate=[generate PGO profile with rustc build]:PROFILE:_files' \
+'--rust-profile-use=[use PGO profile for rustc build]:PROFILE:_files' \
+'--llvm-profile-use=[use PGO profile for LLVM build]:PROFILE:_files' \
+'*--reproducible-artifact=[Additional reproducible artifacts that should be added to the reproducible artifacts archive]:REPRODUCIBLE_ARTIFACT: ' \
+'*--set=[override options in config.toml]:section.option=value:( )' \
+'--open[open the docs in a browser]' \
+'--json[render the documentation in JSON format in addition to the usual HTML format]' \
+'*-v[use verbose output (-vv for very verbose)]' \
+'*--verbose[use verbose output (-vv for very verbose)]' \
+'-i[use incremental compilation]' \
+'--incremental[use incremental compilation]' \
+'--include-default-paths[include default paths in addition to the provided ones]' \
+'--dry-run[dry run; don'\''t build anything]' \
+'--json-output[use message-format=json]' \
+'--llvm-profile-generate[generate PGO profile with llvm built for rustc]' \
+'--enable-bolt-settings[Enable BOLT link flags]' \
+'--skip-stage0-validation[Skip stage0 compiler validation]' \
+'-h[Print help (see more with '\''--help'\'')]' \
+'--help[Print help (see more with '\''--help'\'')]' \
+'*::paths -- paths for the subcommand:_files' \
+&& ret=0
+;;
+(test)
+_arguments "${_arguments_options[@]}" \
+'*--skip=[skips tests matching SUBSTRING, if supported by test tool. May be passed multiple times]:SUBSTRING:_files' \
+'*--test-args=[extra arguments to be passed for the test tool being used (e.g. libtest, compiletest or rustdoc)]:ARGS: ' \
+'*--rustc-args=[extra options to pass the compiler when running tests]:ARGS: ' \
+'--extra-checks=[comma-separated list of other files types to check (accepts py, py\:lint, py\:fmt, shell)]:EXTRA_CHECKS: ' \
+'--compare-mode=[mode describing what file the actual ui output will be compared to]:COMPARE MODE: ' \
+'--pass=[force {check,build,run}-pass tests to this mode]:check | build | run: ' \
+'--run=[whether to execute run-* tests]:auto | always | never: ' \
+'--config=[TOML configuration file for build]:FILE:_files' \
+'--build-dir=[Build directory, overrides \`build.build-dir\` in \`config.toml\`]:DIR:_files -/' \
+'--build=[build target of the stage0 compiler]:BUILD:( )' \
+'--host=[host targets to build]:HOST:( )' \
+'--target=[target targets to build]:TARGET:( )' \
+'*--exclude=[build paths to exclude]:PATH:_files' \
+'--rustc-error-format=[]:RUSTC_ERROR_FORMAT:( )' \
+'--on-fail=[command to run on failure]:CMD:_cmdstring' \
+'--stage=[stage to build (indicates compiler to use/test, e.g., stage 0 uses the bootstrap compiler, stage 1 the stage 0 rustc artifacts, etc.)]:N:( )' \
+'*--keep-stage=[stage(s) to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)]:N:( )' \
+'*--keep-stage-std=[stage(s) of the standard library to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)]:N:( )' \
+'--src=[path to the root of the rust checkout]:DIR:_files -/' \
+'-j+[number of jobs to run in parallel]:JOBS:( )' \
+'--jobs=[number of jobs to run in parallel]:JOBS:( )' \
+'--warnings=[if value is deny, will deny warnings if value is warn, will emit warnings otherwise, use the default configured behaviour]:deny|warn:(deny warn default)' \
+'--error-format=[rustc error format]:FORMAT:( )' \
+'--color=[whether to use color in cargo and rustc output]:STYLE:(always never auto)' \
+'--llvm-skip-rebuild=[whether rebuilding llvm should be skipped, overriding \`skip-rebuld\` in config.toml]:VALUE:(true false)' \
+'--rust-profile-generate=[generate PGO profile with rustc build]:PROFILE:_files' \
+'--rust-profile-use=[use PGO profile for rustc build]:PROFILE:_files' \
+'--llvm-profile-use=[use PGO profile for LLVM build]:PROFILE:_files' \
+'*--reproducible-artifact=[Additional reproducible artifacts that should be added to the reproducible artifacts archive]:REPRODUCIBLE_ARTIFACT: ' \
+'*--set=[override options in config.toml]:section.option=value:( )' \
+'--no-fail-fast[run all tests regardless of failure]' \
+'--no-doc[do not run doc tests]' \
+'--doc[only run doc tests]' \
+'--bless[whether to automatically update stderr/stdout files]' \
+'--force-rerun[rerun tests even if the inputs are unchanged]' \
+'--only-modified[only run tests that result has been changed]' \
+'--rustfix-coverage[enable this to generate a Rustfix coverage file, which is saved in \`/<build_base>/rustfix_missing_coverage.txt\`]' \
+'*-v[use verbose output (-vv for very verbose)]' \
+'*--verbose[use verbose output (-vv for very verbose)]' \
+'-i[use incremental compilation]' \
+'--incremental[use incremental compilation]' \
+'--include-default-paths[include default paths in addition to the provided ones]' \
+'--dry-run[dry run; don'\''t build anything]' \
+'--json-output[use message-format=json]' \
+'--llvm-profile-generate[generate PGO profile with llvm built for rustc]' \
+'--enable-bolt-settings[Enable BOLT link flags]' \
+'--skip-stage0-validation[Skip stage0 compiler validation]' \
+'-h[Print help (see more with '\''--help'\'')]' \
+'--help[Print help (see more with '\''--help'\'')]' \
+'*::paths -- paths for the subcommand:_files' \
+&& ret=0
+;;
+(bench)
+_arguments "${_arguments_options[@]}" \
+'*--test-args=[]:TEST_ARGS: ' \
+'--config=[TOML configuration file for build]:FILE:_files' \
+'--build-dir=[Build directory, overrides \`build.build-dir\` in \`config.toml\`]:DIR:_files -/' \
+'--build=[build target of the stage0 compiler]:BUILD:( )' \
+'--host=[host targets to build]:HOST:( )' \
+'--target=[target targets to build]:TARGET:( )' \
+'*--exclude=[build paths to exclude]:PATH:_files' \
+'*--skip=[build paths to skip]:PATH:_files' \
+'--rustc-error-format=[]:RUSTC_ERROR_FORMAT:( )' \
+'--on-fail=[command to run on failure]:CMD:_cmdstring' \
+'--stage=[stage to build (indicates compiler to use/test, e.g., stage 0 uses the bootstrap compiler, stage 1 the stage 0 rustc artifacts, etc.)]:N:( )' \
+'*--keep-stage=[stage(s) to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)]:N:( )' \
+'*--keep-stage-std=[stage(s) of the standard library to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)]:N:( )' \
+'--src=[path to the root of the rust checkout]:DIR:_files -/' \
+'-j+[number of jobs to run in parallel]:JOBS:( )' \
+'--jobs=[number of jobs to run in parallel]:JOBS:( )' \
+'--warnings=[if value is deny, will deny warnings if value is warn, will emit warnings otherwise, use the default configured behaviour]:deny|warn:(deny warn default)' \
+'--error-format=[rustc error format]:FORMAT:( )' \
+'--color=[whether to use color in cargo and rustc output]:STYLE:(always never auto)' \
+'--llvm-skip-rebuild=[whether rebuilding llvm should be skipped, overriding \`skip-rebuld\` in config.toml]:VALUE:(true false)' \
+'--rust-profile-generate=[generate PGO profile with rustc build]:PROFILE:_files' \
+'--rust-profile-use=[use PGO profile for rustc build]:PROFILE:_files' \
+'--llvm-profile-use=[use PGO profile for LLVM build]:PROFILE:_files' \
+'*--reproducible-artifact=[Additional reproducible artifacts that should be added to the reproducible artifacts archive]:REPRODUCIBLE_ARTIFACT: ' \
+'*--set=[override options in config.toml]:section.option=value:( )' \
+'*-v[use verbose output (-vv for very verbose)]' \
+'*--verbose[use verbose output (-vv for very verbose)]' \
+'-i[use incremental compilation]' \
+'--incremental[use incremental compilation]' \
+'--include-default-paths[include default paths in addition to the provided ones]' \
+'--dry-run[dry run; don'\''t build anything]' \
+'--json-output[use message-format=json]' \
+'--llvm-profile-generate[generate PGO profile with llvm built for rustc]' \
+'--enable-bolt-settings[Enable BOLT link flags]' \
+'--skip-stage0-validation[Skip stage0 compiler validation]' \
+'-h[Print help]' \
+'--help[Print help]' \
+'*::paths -- paths for the subcommand:_files' \
+&& ret=0
+;;
+(clean)
+_arguments "${_arguments_options[@]}" \
+'--stage=[Clean a specific stage without touching other artifacts. By default, every stage is cleaned if this option is not used]:N: ' \
+'--config=[TOML configuration file for build]:FILE:_files' \
+'--build-dir=[Build directory, overrides \`build.build-dir\` in \`config.toml\`]:DIR:_files -/' \
+'--build=[build target of the stage0 compiler]:BUILD:( )' \
+'--host=[host targets to build]:HOST:( )' \
+'--target=[target targets to build]:TARGET:( )' \
+'*--exclude=[build paths to exclude]:PATH:_files' \
+'*--skip=[build paths to skip]:PATH:_files' \
+'--rustc-error-format=[]:RUSTC_ERROR_FORMAT:( )' \
+'--on-fail=[command to run on failure]:CMD:_cmdstring' \
+'*--keep-stage=[stage(s) to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)]:N:( )' \
+'*--keep-stage-std=[stage(s) of the standard library to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)]:N:( )' \
+'--src=[path to the root of the rust checkout]:DIR:_files -/' \
+'-j+[number of jobs to run in parallel]:JOBS:( )' \
+'--jobs=[number of jobs to run in parallel]:JOBS:( )' \
+'--warnings=[if value is deny, will deny warnings if value is warn, will emit warnings otherwise, use the default configured behaviour]:deny|warn:(deny warn default)' \
+'--error-format=[rustc error format]:FORMAT:( )' \
+'--color=[whether to use color in cargo and rustc output]:STYLE:(always never auto)' \
+'--llvm-skip-rebuild=[whether rebuilding llvm should be skipped, overriding \`skip-rebuld\` in config.toml]:VALUE:(true false)' \
+'--rust-profile-generate=[generate PGO profile with rustc build]:PROFILE:_files' \
+'--rust-profile-use=[use PGO profile for rustc build]:PROFILE:_files' \
+'--llvm-profile-use=[use PGO profile for LLVM build]:PROFILE:_files' \
+'*--reproducible-artifact=[Additional reproducible artifacts that should be added to the reproducible artifacts archive]:REPRODUCIBLE_ARTIFACT: ' \
+'*--set=[override options in config.toml]:section.option=value:( )' \
+'--all[Clean the entire build directory (not used by default)]' \
+'*-v[use verbose output (-vv for very verbose)]' \
+'*--verbose[use verbose output (-vv for very verbose)]' \
+'-i[use incremental compilation]' \
+'--incremental[use incremental compilation]' \
+'--include-default-paths[include default paths in addition to the provided ones]' \
+'--dry-run[dry run; don'\''t build anything]' \
+'--json-output[use message-format=json]' \
+'--llvm-profile-generate[generate PGO profile with llvm built for rustc]' \
+'--enable-bolt-settings[Enable BOLT link flags]' \
+'--skip-stage0-validation[Skip stage0 compiler validation]' \
+'-h[Print help]' \
+'--help[Print help]' \
+'*::paths -- paths for the subcommand:_files' \
+&& ret=0
+;;
+(dist)
+_arguments "${_arguments_options[@]}" \
+'--config=[TOML configuration file for build]:FILE:_files' \
+'--build-dir=[Build directory, overrides \`build.build-dir\` in \`config.toml\`]:DIR:_files -/' \
+'--build=[build target of the stage0 compiler]:BUILD:( )' \
+'--host=[host targets to build]:HOST:( )' \
+'--target=[target targets to build]:TARGET:( )' \
+'*--exclude=[build paths to exclude]:PATH:_files' \
+'*--skip=[build paths to skip]:PATH:_files' \
+'--rustc-error-format=[]:RUSTC_ERROR_FORMAT:( )' \
+'--on-fail=[command to run on failure]:CMD:_cmdstring' \
+'--stage=[stage to build (indicates compiler to use/test, e.g., stage 0 uses the bootstrap compiler, stage 1 the stage 0 rustc artifacts, etc.)]:N:( )' \
+'*--keep-stage=[stage(s) to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)]:N:( )' \
+'*--keep-stage-std=[stage(s) of the standard library to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)]:N:( )' \
+'--src=[path to the root of the rust checkout]:DIR:_files -/' \
+'-j+[number of jobs to run in parallel]:JOBS:( )' \
+'--jobs=[number of jobs to run in parallel]:JOBS:( )' \
+'--warnings=[if value is deny, will deny warnings if value is warn, will emit warnings otherwise, use the default configured behaviour]:deny|warn:(deny warn default)' \
+'--error-format=[rustc error format]:FORMAT:( )' \
+'--color=[whether to use color in cargo and rustc output]:STYLE:(always never auto)' \
+'--llvm-skip-rebuild=[whether rebuilding llvm should be skipped, overriding \`skip-rebuld\` in config.toml]:VALUE:(true false)' \
+'--rust-profile-generate=[generate PGO profile with rustc build]:PROFILE:_files' \
+'--rust-profile-use=[use PGO profile for rustc build]:PROFILE:_files' \
+'--llvm-profile-use=[use PGO profile for LLVM build]:PROFILE:_files' \
+'*--reproducible-artifact=[Additional reproducible artifacts that should be added to the reproducible artifacts archive]:REPRODUCIBLE_ARTIFACT: ' \
+'*--set=[override options in config.toml]:section.option=value:( )' \
+'*-v[use verbose output (-vv for very verbose)]' \
+'*--verbose[use verbose output (-vv for very verbose)]' \
+'-i[use incremental compilation]' \
+'--incremental[use incremental compilation]' \
+'--include-default-paths[include default paths in addition to the provided ones]' \
+'--dry-run[dry run; don'\''t build anything]' \
+'--json-output[use message-format=json]' \
+'--llvm-profile-generate[generate PGO profile with llvm built for rustc]' \
+'--enable-bolt-settings[Enable BOLT link flags]' \
+'--skip-stage0-validation[Skip stage0 compiler validation]' \
+'-h[Print help]' \
+'--help[Print help]' \
+'*::paths -- paths for the subcommand:_files' \
+&& ret=0
+;;
+(install)
+_arguments "${_arguments_options[@]}" \
+'--config=[TOML configuration file for build]:FILE:_files' \
+'--build-dir=[Build directory, overrides \`build.build-dir\` in \`config.toml\`]:DIR:_files -/' \
+'--build=[build target of the stage0 compiler]:BUILD:( )' \
+'--host=[host targets to build]:HOST:( )' \
+'--target=[target targets to build]:TARGET:( )' \
+'*--exclude=[build paths to exclude]:PATH:_files' \
+'*--skip=[build paths to skip]:PATH:_files' \
+'--rustc-error-format=[]:RUSTC_ERROR_FORMAT:( )' \
+'--on-fail=[command to run on failure]:CMD:_cmdstring' \
+'--stage=[stage to build (indicates compiler to use/test, e.g., stage 0 uses the bootstrap compiler, stage 1 the stage 0 rustc artifacts, etc.)]:N:( )' \
+'*--keep-stage=[stage(s) to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)]:N:( )' \
+'*--keep-stage-std=[stage(s) of the standard library to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)]:N:( )' \
+'--src=[path to the root of the rust checkout]:DIR:_files -/' \
+'-j+[number of jobs to run in parallel]:JOBS:( )' \
+'--jobs=[number of jobs to run in parallel]:JOBS:( )' \
+'--warnings=[if value is deny, will deny warnings if value is warn, will emit warnings otherwise, use the default configured behaviour]:deny|warn:(deny warn default)' \
+'--error-format=[rustc error format]:FORMAT:( )' \
+'--color=[whether to use color in cargo and rustc output]:STYLE:(always never auto)' \
+'--llvm-skip-rebuild=[whether rebuilding llvm should be skipped, overriding \`skip-rebuld\` in config.toml]:VALUE:(true false)' \
+'--rust-profile-generate=[generate PGO profile with rustc build]:PROFILE:_files' \
+'--rust-profile-use=[use PGO profile for rustc build]:PROFILE:_files' \
+'--llvm-profile-use=[use PGO profile for LLVM build]:PROFILE:_files' \
+'*--reproducible-artifact=[Additional reproducible artifacts that should be added to the reproducible artifacts archive]:REPRODUCIBLE_ARTIFACT: ' \
+'*--set=[override options in config.toml]:section.option=value:( )' \
+'*-v[use verbose output (-vv for very verbose)]' \
+'*--verbose[use verbose output (-vv for very verbose)]' \
+'-i[use incremental compilation]' \
+'--incremental[use incremental compilation]' \
+'--include-default-paths[include default paths in addition to the provided ones]' \
+'--dry-run[dry run; don'\''t build anything]' \
+'--json-output[use message-format=json]' \
+'--llvm-profile-generate[generate PGO profile with llvm built for rustc]' \
+'--enable-bolt-settings[Enable BOLT link flags]' \
+'--skip-stage0-validation[Skip stage0 compiler validation]' \
+'-h[Print help]' \
+'--help[Print help]' \
+'*::paths -- paths for the subcommand:_files' \
+&& ret=0
+;;
+(run)
+_arguments "${_arguments_options[@]}" \
+'*--args=[arguments for the tool]:ARGS: ' \
+'--config=[TOML configuration file for build]:FILE:_files' \
+'--build-dir=[Build directory, overrides \`build.build-dir\` in \`config.toml\`]:DIR:_files -/' \
+'--build=[build target of the stage0 compiler]:BUILD:( )' \
+'--host=[host targets to build]:HOST:( )' \
+'--target=[target targets to build]:TARGET:( )' \
+'*--exclude=[build paths to exclude]:PATH:_files' \
+'*--skip=[build paths to skip]:PATH:_files' \
+'--rustc-error-format=[]:RUSTC_ERROR_FORMAT:( )' \
+'--on-fail=[command to run on failure]:CMD:_cmdstring' \
+'--stage=[stage to build (indicates compiler to use/test, e.g., stage 0 uses the bootstrap compiler, stage 1 the stage 0 rustc artifacts, etc.)]:N:( )' \
+'*--keep-stage=[stage(s) to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)]:N:( )' \
+'*--keep-stage-std=[stage(s) of the standard library to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)]:N:( )' \
+'--src=[path to the root of the rust checkout]:DIR:_files -/' \
+'-j+[number of jobs to run in parallel]:JOBS:( )' \
+'--jobs=[number of jobs to run in parallel]:JOBS:( )' \
+'--warnings=[if value is deny, will deny warnings if value is warn, will emit warnings otherwise, use the default configured behaviour]:deny|warn:(deny warn default)' \
+'--error-format=[rustc error format]:FORMAT:( )' \
+'--color=[whether to use color in cargo and rustc output]:STYLE:(always never auto)' \
+'--llvm-skip-rebuild=[whether rebuilding llvm should be skipped, overriding \`skip-rebuld\` in config.toml]:VALUE:(true false)' \
+'--rust-profile-generate=[generate PGO profile with rustc build]:PROFILE:_files' \
+'--rust-profile-use=[use PGO profile for rustc build]:PROFILE:_files' \
+'--llvm-profile-use=[use PGO profile for LLVM build]:PROFILE:_files' \
+'*--reproducible-artifact=[Additional reproducible artifacts that should be added to the reproducible artifacts archive]:REPRODUCIBLE_ARTIFACT: ' \
+'*--set=[override options in config.toml]:section.option=value:( )' \
+'*-v[use verbose output (-vv for very verbose)]' \
+'*--verbose[use verbose output (-vv for very verbose)]' \
+'-i[use incremental compilation]' \
+'--incremental[use incremental compilation]' \
+'--include-default-paths[include default paths in addition to the provided ones]' \
+'--dry-run[dry run; don'\''t build anything]' \
+'--json-output[use message-format=json]' \
+'--llvm-profile-generate[generate PGO profile with llvm built for rustc]' \
+'--enable-bolt-settings[Enable BOLT link flags]' \
+'--skip-stage0-validation[Skip stage0 compiler validation]' \
+'-h[Print help (see more with '\''--help'\'')]' \
+'--help[Print help (see more with '\''--help'\'')]' \
+'*::paths -- paths for the subcommand:_files' \
+&& ret=0
+;;
+(setup)
+_arguments "${_arguments_options[@]}" \
+'--config=[TOML configuration file for build]:FILE:_files' \
+'--build-dir=[Build directory, overrides \`build.build-dir\` in \`config.toml\`]:DIR:_files -/' \
+'--build=[build target of the stage0 compiler]:BUILD:( )' \
+'--host=[host targets to build]:HOST:( )' \
+'--target=[target targets to build]:TARGET:( )' \
+'*--exclude=[build paths to exclude]:PATH:_files' \
+'*--skip=[build paths to skip]:PATH:_files' \
+'--rustc-error-format=[]:RUSTC_ERROR_FORMAT:( )' \
+'--on-fail=[command to run on failure]:CMD:_cmdstring' \
+'--stage=[stage to build (indicates compiler to use/test, e.g., stage 0 uses the bootstrap compiler, stage 1 the stage 0 rustc artifacts, etc.)]:N:( )' \
+'*--keep-stage=[stage(s) to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)]:N:( )' \
+'*--keep-stage-std=[stage(s) of the standard library to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)]:N:( )' \
+'--src=[path to the root of the rust checkout]:DIR:_files -/' \
+'-j+[number of jobs to run in parallel]:JOBS:( )' \
+'--jobs=[number of jobs to run in parallel]:JOBS:( )' \
+'--warnings=[if value is deny, will deny warnings if value is warn, will emit warnings otherwise, use the default configured behaviour]:deny|warn:(deny warn default)' \
+'--error-format=[rustc error format]:FORMAT:( )' \
+'--color=[whether to use color in cargo and rustc output]:STYLE:(always never auto)' \
+'--llvm-skip-rebuild=[whether rebuilding llvm should be skipped, overriding \`skip-rebuld\` in config.toml]:VALUE:(true false)' \
+'--rust-profile-generate=[generate PGO profile with rustc build]:PROFILE:_files' \
+'--rust-profile-use=[use PGO profile for rustc build]:PROFILE:_files' \
+'--llvm-profile-use=[use PGO profile for LLVM build]:PROFILE:_files' \
+'*--reproducible-artifact=[Additional reproducible artifacts that should be added to the reproducible artifacts archive]:REPRODUCIBLE_ARTIFACT: ' \
+'*--set=[override options in config.toml]:section.option=value:( )' \
+'*-v[use verbose output (-vv for very verbose)]' \
+'*--verbose[use verbose output (-vv for very verbose)]' \
+'-i[use incremental compilation]' \
+'--incremental[use incremental compilation]' \
+'--include-default-paths[include default paths in addition to the provided ones]' \
+'--dry-run[dry run; don'\''t build anything]' \
+'--json-output[use message-format=json]' \
+'--llvm-profile-generate[generate PGO profile with llvm built for rustc]' \
+'--enable-bolt-settings[Enable BOLT link flags]' \
+'--skip-stage0-validation[Skip stage0 compiler validation]' \
+'-h[Print help (see more with '\''--help'\'')]' \
+'--help[Print help (see more with '\''--help'\'')]' \
+'::profile -- Either the profile for `config.toml` or another setup action. May be omitted to set up interactively:_files' \
+'*::paths -- paths for the subcommand:_files' \
+&& ret=0
+;;
+(suggest)
+_arguments "${_arguments_options[@]}" \
+'--config=[TOML configuration file for build]:FILE:_files' \
+'--build-dir=[Build directory, overrides \`build.build-dir\` in \`config.toml\`]:DIR:_files -/' \
+'--build=[build target of the stage0 compiler]:BUILD:( )' \
+'--host=[host targets to build]:HOST:( )' \
+'--target=[target targets to build]:TARGET:( )' \
+'*--exclude=[build paths to exclude]:PATH:_files' \
+'*--skip=[build paths to skip]:PATH:_files' \
+'--rustc-error-format=[]:RUSTC_ERROR_FORMAT:( )' \
+'--on-fail=[command to run on failure]:CMD:_cmdstring' \
+'--stage=[stage to build (indicates compiler to use/test, e.g., stage 0 uses the bootstrap compiler, stage 1 the stage 0 rustc artifacts, etc.)]:N:( )' \
+'*--keep-stage=[stage(s) to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)]:N:( )' \
+'*--keep-stage-std=[stage(s) of the standard library to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)]:N:( )' \
+'--src=[path to the root of the rust checkout]:DIR:_files -/' \
+'-j+[number of jobs to run in parallel]:JOBS:( )' \
+'--jobs=[number of jobs to run in parallel]:JOBS:( )' \
+'--warnings=[if value is deny, will deny warnings if value is warn, will emit warnings otherwise, use the default configured behaviour]:deny|warn:(deny warn default)' \
+'--error-format=[rustc error format]:FORMAT:( )' \
+'--color=[whether to use color in cargo and rustc output]:STYLE:(always never auto)' \
+'--llvm-skip-rebuild=[whether rebuilding llvm should be skipped, overriding \`skip-rebuld\` in config.toml]:VALUE:(true false)' \
+'--rust-profile-generate=[generate PGO profile with rustc build]:PROFILE:_files' \
+'--rust-profile-use=[use PGO profile for rustc build]:PROFILE:_files' \
+'--llvm-profile-use=[use PGO profile for LLVM build]:PROFILE:_files' \
+'*--reproducible-artifact=[Additional reproducible artifacts that should be added to the reproducible artifacts archive]:REPRODUCIBLE_ARTIFACT: ' \
+'*--set=[override options in config.toml]:section.option=value:( )' \
+'--run[run suggested tests]' \
+'*-v[use verbose output (-vv for very verbose)]' \
+'*--verbose[use verbose output (-vv for very verbose)]' \
+'-i[use incremental compilation]' \
+'--incremental[use incremental compilation]' \
+'--include-default-paths[include default paths in addition to the provided ones]' \
+'--dry-run[dry run; don'\''t build anything]' \
+'--json-output[use message-format=json]' \
+'--llvm-profile-generate[generate PGO profile with llvm built for rustc]' \
+'--enable-bolt-settings[Enable BOLT link flags]' \
+'--skip-stage0-validation[Skip stage0 compiler validation]' \
+'-h[Print help (see more with '\''--help'\'')]' \
+'--help[Print help (see more with '\''--help'\'')]' \
+'*::paths -- paths for the subcommand:_files' \
+&& ret=0
+;;
+ esac
+ ;;
+esac
+}
+
+(( $+functions[_x.py_commands] )) ||
+_x.py_commands() {
+ local commands; commands=(
+'build:Compile either the compiler or libraries' \
+'check:Compile either the compiler or libraries, using cargo check' \
+'clippy:Run Clippy (uses rustup/cargo-installed clippy binary)' \
+'fix:Run cargo fix' \
+'fmt:Run rustfmt' \
+'doc:Build documentation' \
+'test:Build and run some test suites' \
+'bench:Build and run some benchmarks' \
+'clean:Clean out build directories' \
+'dist:Build distribution artifacts' \
+'install:Install distribution artifacts' \
+'run:Run tools contained in this repository' \
+'setup:Set up the environment for development' \
+'suggest:Suggest a subset of tests to run, based on modified files' \
+ )
+ _describe -t commands 'x.py commands' commands "$@"
+}
+(( $+functions[_x.py__bench_commands] )) ||
+_x.py__bench_commands() {
+ local commands; commands=()
+ _describe -t commands 'x.py bench commands' commands "$@"
+}
+(( $+functions[_x.py__build_commands] )) ||
+_x.py__build_commands() {
+ local commands; commands=()
+ _describe -t commands 'x.py build commands' commands "$@"
+}
+(( $+functions[_x.py__check_commands] )) ||
+_x.py__check_commands() {
+ local commands; commands=()
+ _describe -t commands 'x.py check commands' commands "$@"
+}
+(( $+functions[_x.py__clean_commands] )) ||
+_x.py__clean_commands() {
+ local commands; commands=()
+ _describe -t commands 'x.py clean commands' commands "$@"
+}
+(( $+functions[_x.py__clippy_commands] )) ||
+_x.py__clippy_commands() {
+ local commands; commands=()
+ _describe -t commands 'x.py clippy commands' commands "$@"
+}
+(( $+functions[_x.py__dist_commands] )) ||
+_x.py__dist_commands() {
+ local commands; commands=()
+ _describe -t commands 'x.py dist commands' commands "$@"
+}
+(( $+functions[_x.py__doc_commands] )) ||
+_x.py__doc_commands() {
+ local commands; commands=()
+ _describe -t commands 'x.py doc commands' commands "$@"
+}
+(( $+functions[_x.py__fix_commands] )) ||
+_x.py__fix_commands() {
+ local commands; commands=()
+ _describe -t commands 'x.py fix commands' commands "$@"
+}
+(( $+functions[_x.py__fmt_commands] )) ||
+_x.py__fmt_commands() {
+ local commands; commands=()
+ _describe -t commands 'x.py fmt commands' commands "$@"
+}
+(( $+functions[_x.py__install_commands] )) ||
+_x.py__install_commands() {
+ local commands; commands=()
+ _describe -t commands 'x.py install commands' commands "$@"
+}
+(( $+functions[_x.py__run_commands] )) ||
+_x.py__run_commands() {
+ local commands; commands=()
+ _describe -t commands 'x.py run commands' commands "$@"
+}
+(( $+functions[_x.py__setup_commands] )) ||
+_x.py__setup_commands() {
+ local commands; commands=()
+ _describe -t commands 'x.py setup commands' commands "$@"
+}
+(( $+functions[_x.py__suggest_commands] )) ||
+_x.py__suggest_commands() {
+ local commands; commands=()
+ _describe -t commands 'x.py suggest commands' commands "$@"
+}
+(( $+functions[_x.py__test_commands] )) ||
+_x.py__test_commands() {
+ local commands; commands=()
+ _describe -t commands 'x.py test commands' commands "$@"
+}
+
+if [ "$funcstack[1]" = "_x.py" ]; then
+ _x.py "$@"
+else
+ compdef _x.py x.py
+fi
diff --git a/src/etc/gdb_lookup.py b/src/etc/gdb_lookup.py
index 8171cb4e9..f3ac9c109 100644
--- a/src/etc/gdb_lookup.py
+++ b/src/etc/gdb_lookup.py
@@ -1,4 +1,5 @@
import gdb
+import gdb.printing
import re
from gdb_providers import *
@@ -9,7 +10,7 @@ _gdb_version_matched = re.search('([0-9]+)\\.([0-9]+)', gdb.VERSION)
gdb_version = [int(num) for num in _gdb_version_matched.groups()] if _gdb_version_matched else []
def register_printers(objfile):
- objfile.pretty_printers.append(lookup)
+ objfile.pretty_printers.append(printer)
# BACKCOMPAT: rust 1.35
@@ -38,58 +39,80 @@ def check_enum_discriminant(valobj):
return True
-def lookup(valobj):
- rust_type = classify_rust_type(valobj.type)
-
- if rust_type == RustType.ENUM:
- # use enum provider only for GDB <7.12
- if gdb_version[0] < 7 or (gdb_version[0] == 7 and gdb_version[1] < 12):
- if check_enum_discriminant(valobj):
- return EnumProvider(valobj)
-
- if rust_type == RustType.STD_STRING:
- return StdStringProvider(valobj)
- if rust_type == RustType.STD_OS_STRING:
- return StdOsStringProvider(valobj)
- if rust_type == RustType.STD_STR:
- return StdStrProvider(valobj)
- if rust_type == RustType.STD_SLICE:
- return StdSliceProvider(valobj)
- if rust_type == RustType.STD_VEC:
- return StdVecProvider(valobj)
- if rust_type == RustType.STD_VEC_DEQUE:
- return StdVecDequeProvider(valobj)
- if rust_type == RustType.STD_BTREE_SET:
- return StdBTreeSetProvider(valobj)
- if rust_type == RustType.STD_BTREE_MAP:
- return StdBTreeMapProvider(valobj)
- if rust_type == RustType.STD_HASH_MAP:
- if is_hashbrown_hashmap(valobj):
- return StdHashMapProvider(valobj)
- else:
- return StdOldHashMapProvider(valobj)
- if rust_type == RustType.STD_HASH_SET:
- hash_map = valobj[valobj.type.fields()[0]]
- if is_hashbrown_hashmap(hash_map):
- return StdHashMapProvider(valobj, show_values=False)
- else:
- return StdOldHashMapProvider(hash_map, show_values=False)
-
- if rust_type == RustType.STD_RC:
- return StdRcProvider(valobj)
- if rust_type == RustType.STD_ARC:
- return StdRcProvider(valobj, is_atomic=True)
-
- if rust_type == RustType.STD_CELL:
- return StdCellProvider(valobj)
- if rust_type == RustType.STD_REF:
- return StdRefProvider(valobj)
- if rust_type == RustType.STD_REF_MUT:
- return StdRefProvider(valobj)
- if rust_type == RustType.STD_REF_CELL:
- return StdRefCellProvider(valobj)
-
- if rust_type == RustType.STD_NONZERO_NUMBER:
- return StdNonZeroNumberProvider(valobj)
-
+# Helper for enum printing that checks the discriminant. Only used in
+# older gdb.
+def enum_provider(valobj):
+ if check_enum_discriminant(valobj):
+ return EnumProvider(valobj)
return None
+
+
+# Helper to handle both old and new hash maps.
+def hashmap_provider(valobj):
+ if is_hashbrown_hashmap(valobj):
+ return StdHashMapProvider(valobj)
+ else:
+ return StdOldHashMapProvider(valobj)
+
+
+# Helper to handle both old and new hash sets.
+def hashset_provider(valobj):
+ hash_map = valobj[valobj.type.fields()[0]]
+ if is_hashbrown_hashmap(hash_map):
+ return StdHashMapProvider(valobj, show_values=False)
+ else:
+ return StdOldHashMapProvider(hash_map, show_values=False)
+
+
+class PrintByRustType(gdb.printing.SubPrettyPrinter):
+ def __init__(self, name, provider):
+ super(PrintByRustType, self).__init__(name)
+ self.provider = provider
+
+ def __call__(self, val):
+ if self.enabled:
+ return self.provider(val)
+ return None
+
+
+class RustPrettyPrinter(gdb.printing.PrettyPrinter):
+ def __init__(self, name):
+ super(RustPrettyPrinter, self).__init__(name, [])
+ self.type_map = {}
+
+ def add(self, rust_type, provider):
+ # Just use the rust_type as the name.
+ printer = PrintByRustType(rust_type, provider)
+ self.type_map[rust_type] = printer
+ self.subprinters.append(printer)
+
+ def __call__(self, valobj):
+ rust_type = classify_rust_type(valobj.type)
+ if rust_type in self.type_map:
+ return self.type_map[rust_type](valobj)
+ return None
+
+
+printer = RustPrettyPrinter("rust")
+# use enum provider only for GDB <7.12
+if gdb_version[0] < 7 or (gdb_version[0] == 7 and gdb_version[1] < 12):
+ printer.add(RustType.ENUM, enum_provider)
+printer.add(RustType.STD_STRING, StdStringProvider)
+printer.add(RustType.STD_OS_STRING, StdOsStringProvider)
+printer.add(RustType.STD_STR, StdStrProvider)
+printer.add(RustType.STD_SLICE, StdSliceProvider)
+printer.add(RustType.STD_VEC, StdVecProvider)
+printer.add(RustType.STD_VEC_DEQUE, StdVecDequeProvider)
+printer.add(RustType.STD_BTREE_SET, StdBTreeSetProvider)
+printer.add(RustType.STD_BTREE_MAP, StdBTreeMapProvider)
+printer.add(RustType.STD_HASH_MAP, hashmap_provider)
+printer.add(RustType.STD_HASH_SET, hashset_provider)
+printer.add(RustType.STD_RC, StdRcProvider)
+printer.add(RustType.STD_ARC, lambda valobj: StdRcProvider(valobj, is_atomic=True))
+
+printer.add(RustType.STD_CELL, StdCellProvider)
+printer.add(RustType.STD_REF, StdRefProvider)
+printer.add(RustType.STD_REF_MUT, StdRefProvider)
+printer.add(RustType.STD_REF_CELL, StdRefCellProvider)
+
+printer.add(RustType.STD_NONZERO_NUMBER, StdNonZeroNumberProvider)
diff --git a/src/etc/gdb_providers.py b/src/etc/gdb_providers.py
index 32b8d8e24..e851aa626 100644
--- a/src/etc/gdb_providers.py
+++ b/src/etc/gdb_providers.py
@@ -18,70 +18,79 @@ def unwrap_unique_or_non_null(unique_or_nonnull):
return ptr if ptr.type.code == gdb.TYPE_CODE_PTR else ptr[ptr.type.fields()[0]]
-class EnumProvider:
+# GDB 14 has a tag class that indicates that extension methods are ok
+# to call. Use of this tag only requires that printers hide local
+# attributes and methods by prefixing them with "_".
+if hasattr(gdb, 'ValuePrinter'):
+ printer_base = gdb.ValuePrinter
+else:
+ printer_base = object
+
+
+class EnumProvider(printer_base):
def __init__(self, valobj):
content = valobj[valobj.type.fields()[0]]
fields = content.type.fields()
- self.empty = len(fields) == 0
- if not self.empty:
+ self._empty = len(fields) == 0
+ if not self._empty:
if len(fields) == 1:
discriminant = 0
else:
discriminant = int(content[fields[0]]) + 1
- self.active_variant = content[fields[discriminant]]
- self.name = fields[discriminant].name
- self.full_name = "{}::{}".format(valobj.type.name, self.name)
+ self._active_variant = content[fields[discriminant]]
+ self._name = fields[discriminant].name
+ self._full_name = "{}::{}".format(valobj.type.name, self._name)
else:
- self.full_name = valobj.type.name
+ self._full_name = valobj.type.name
def to_string(self):
- return self.full_name
+ return self._full_name
def children(self):
- if not self.empty:
- yield self.name, self.active_variant
+ if not self._empty:
+ yield self._name, self._active_variant
-class StdStringProvider:
+class StdStringProvider(printer_base):
def __init__(self, valobj):
- self.valobj = valobj
+ self._valobj = valobj
vec = valobj["vec"]
- self.length = int(vec["len"])
- self.data_ptr = unwrap_unique_or_non_null(vec["buf"]["ptr"])
+ self._length = int(vec["len"])
+ self._data_ptr = unwrap_unique_or_non_null(vec["buf"]["ptr"])
def to_string(self):
- return self.data_ptr.lazy_string(encoding="utf-8", length=self.length)
+ return self._data_ptr.lazy_string(encoding="utf-8", length=self._length)
@staticmethod
def display_hint():
return "string"
-class StdOsStringProvider:
+class StdOsStringProvider(printer_base):
def __init__(self, valobj):
- self.valobj = valobj
- buf = self.valobj["inner"]["inner"]
+ self._valobj = valobj
+ buf = self._valobj["inner"]["inner"]
is_windows = "Wtf8Buf" in buf.type.name
vec = buf[ZERO_FIELD] if is_windows else buf
- self.length = int(vec["len"])
- self.data_ptr = unwrap_unique_or_non_null(vec["buf"]["ptr"])
+ self._length = int(vec["len"])
+ self._data_ptr = unwrap_unique_or_non_null(vec["buf"]["ptr"])
def to_string(self):
- return self.data_ptr.lazy_string(encoding="utf-8", length=self.length)
+ return self._data_ptr.lazy_string(encoding="utf-8", length=self._length)
def display_hint(self):
return "string"
-class StdStrProvider:
+class StdStrProvider(printer_base):
def __init__(self, valobj):
- self.valobj = valobj
- self.length = int(valobj["length"])
- self.data_ptr = valobj["data_ptr"]
+ self._valobj = valobj
+ self._length = int(valobj["length"])
+ self._data_ptr = valobj["data_ptr"]
def to_string(self):
- return self.data_ptr.lazy_string(encoding="utf-8", length=self.length)
+ return self._data_ptr.lazy_string(encoding="utf-8", length=self._length)
@staticmethod
def display_hint():
@@ -103,36 +112,36 @@ def _enumerate_array_elements(element_ptrs):
yield key, element
-class StdSliceProvider:
+class StdSliceProvider(printer_base):
def __init__(self, valobj):
- self.valobj = valobj
- self.length = int(valobj["length"])
- self.data_ptr = valobj["data_ptr"]
+ self._valobj = valobj
+ self._length = int(valobj["length"])
+ self._data_ptr = valobj["data_ptr"]
def to_string(self):
- return "{}(size={})".format(self.valobj.type, self.length)
+ return "{}(size={})".format(self._valobj.type, self._length)
def children(self):
return _enumerate_array_elements(
- self.data_ptr + index for index in xrange(self.length)
+ self._data_ptr + index for index in xrange(self._length)
)
@staticmethod
def display_hint():
return "array"
-class StdVecProvider:
+class StdVecProvider(printer_base):
def __init__(self, valobj):
- self.valobj = valobj
- self.length = int(valobj["len"])
- self.data_ptr = unwrap_unique_or_non_null(valobj["buf"]["ptr"])
+ self._valobj = valobj
+ self._length = int(valobj["len"])
+ self._data_ptr = unwrap_unique_or_non_null(valobj["buf"]["ptr"])
def to_string(self):
- return "Vec(size={})".format(self.length)
+ return "Vec(size={})".format(self._length)
def children(self):
return _enumerate_array_elements(
- self.data_ptr + index for index in xrange(self.length)
+ self._data_ptr + index for index in xrange(self._length)
)
@staticmethod
@@ -140,20 +149,20 @@ class StdVecProvider:
return "array"
-class StdVecDequeProvider:
+class StdVecDequeProvider(printer_base):
def __init__(self, valobj):
- self.valobj = valobj
- self.head = int(valobj["head"])
- self.size = int(valobj["len"])
- self.cap = int(valobj["buf"]["cap"])
- self.data_ptr = unwrap_unique_or_non_null(valobj["buf"]["ptr"])
+ self._valobj = valobj
+ self._head = int(valobj["head"])
+ self._size = int(valobj["len"])
+ self._cap = int(valobj["buf"]["cap"])
+ self._data_ptr = unwrap_unique_or_non_null(valobj["buf"]["ptr"])
def to_string(self):
- return "VecDeque(size={})".format(self.size)
+ return "VecDeque(size={})".format(self._size)
def children(self):
return _enumerate_array_elements(
- (self.data_ptr + ((self.head + index) % self.cap)) for index in xrange(self.size)
+ (self._data_ptr + ((self._head + index) % self._cap)) for index in xrange(self._size)
)
@staticmethod
@@ -161,81 +170,81 @@ class StdVecDequeProvider:
return "array"
-class StdRcProvider:
+class StdRcProvider(printer_base):
def __init__(self, valobj, is_atomic=False):
- self.valobj = valobj
- self.is_atomic = is_atomic
- self.ptr = unwrap_unique_or_non_null(valobj["ptr"])
- self.value = self.ptr["data" if is_atomic else "value"]
- self.strong = self.ptr["strong"]["v" if is_atomic else "value"]["value"]
- self.weak = self.ptr["weak"]["v" if is_atomic else "value"]["value"] - 1
+ self._valobj = valobj
+ self._is_atomic = is_atomic
+ self._ptr = unwrap_unique_or_non_null(valobj["ptr"])
+ self._value = self._ptr["data" if is_atomic else "value"]
+ self._strong = self._ptr["strong"]["v" if is_atomic else "value"]["value"]
+ self._weak = self._ptr["weak"]["v" if is_atomic else "value"]["value"] - 1
def to_string(self):
- if self.is_atomic:
- return "Arc(strong={}, weak={})".format(int(self.strong), int(self.weak))
+ if self._is_atomic:
+ return "Arc(strong={}, weak={})".format(int(self._strong), int(self._weak))
else:
- return "Rc(strong={}, weak={})".format(int(self.strong), int(self.weak))
+ return "Rc(strong={}, weak={})".format(int(self._strong), int(self._weak))
def children(self):
- yield "value", self.value
- yield "strong", self.strong
- yield "weak", self.weak
+ yield "value", self._value
+ yield "strong", self._strong
+ yield "weak", self._weak
-class StdCellProvider:
+class StdCellProvider(printer_base):
def __init__(self, valobj):
- self.value = valobj["value"]["value"]
+ self._value = valobj["value"]["value"]
def to_string(self):
return "Cell"
def children(self):
- yield "value", self.value
+ yield "value", self._value
-class StdRefProvider:
+class StdRefProvider(printer_base):
def __init__(self, valobj):
- self.value = valobj["value"].dereference()
- self.borrow = valobj["borrow"]["borrow"]["value"]["value"]
+ self._value = valobj["value"].dereference()
+ self._borrow = valobj["borrow"]["borrow"]["value"]["value"]
def to_string(self):
- borrow = int(self.borrow)
+ borrow = int(self._borrow)
if borrow >= 0:
return "Ref(borrow={})".format(borrow)
else:
return "Ref(borrow_mut={})".format(-borrow)
def children(self):
- yield "*value", self.value
- yield "borrow", self.borrow
+ yield "*value", self._value
+ yield "borrow", self._borrow
-class StdRefCellProvider:
+class StdRefCellProvider(printer_base):
def __init__(self, valobj):
- self.value = valobj["value"]["value"]
- self.borrow = valobj["borrow"]["value"]["value"]
+ self._value = valobj["value"]["value"]
+ self._borrow = valobj["borrow"]["value"]["value"]
def to_string(self):
- borrow = int(self.borrow)
+ borrow = int(self._borrow)
if borrow >= 0:
return "RefCell(borrow={})".format(borrow)
else:
return "RefCell(borrow_mut={})".format(-borrow)
def children(self):
- yield "value", self.value
- yield "borrow", self.borrow
+ yield "value", self._value
+ yield "borrow", self._borrow
-class StdNonZeroNumberProvider:
+class StdNonZeroNumberProvider(printer_base):
def __init__(self, valobj):
fields = valobj.type.fields()
assert len(fields) == 1
field = list(fields)[0]
- self.value = str(valobj[field.name])
+ self._value = str(valobj[field.name])
def to_string(self):
- return self.value
+ return self._value
# Yields children (in a provider's sense of the word) for a BTreeMap.
@@ -280,15 +289,15 @@ def children_of_btree_map(map):
yield child
-class StdBTreeSetProvider:
+class StdBTreeSetProvider(printer_base):
def __init__(self, valobj):
- self.valobj = valobj
+ self._valobj = valobj
def to_string(self):
- return "BTreeSet(size={})".format(self.valobj["map"]["length"])
+ return "BTreeSet(size={})".format(self._valobj["map"]["length"])
def children(self):
- inner_map = self.valobj["map"]
+ inner_map = self._valobj["map"]
for i, (child, _) in enumerate(children_of_btree_map(inner_map)):
yield "[{}]".format(i), child
@@ -297,15 +306,15 @@ class StdBTreeSetProvider:
return "array"
-class StdBTreeMapProvider:
+class StdBTreeMapProvider(printer_base):
def __init__(self, valobj):
- self.valobj = valobj
+ self._valobj = valobj
def to_string(self):
- return "BTreeMap(size={})".format(self.valobj["length"])
+ return "BTreeMap(size={})".format(self._valobj["length"])
def children(self):
- for i, (key, val) in enumerate(children_of_btree_map(self.valobj)):
+ for i, (key, val) in enumerate(children_of_btree_map(self._valobj)):
yield "key{}".format(i), key
yield "val{}".format(i), val
@@ -315,124 +324,124 @@ class StdBTreeMapProvider:
# BACKCOMPAT: rust 1.35
-class StdOldHashMapProvider:
+class StdOldHashMapProvider(printer_base):
def __init__(self, valobj, show_values=True):
- self.valobj = valobj
- self.show_values = show_values
-
- self.table = self.valobj["table"]
- self.size = int(self.table["size"])
- self.hashes = self.table["hashes"]
- self.hash_uint_type = self.hashes.type
- self.hash_uint_size = self.hashes.type.sizeof
- self.modulo = 2 ** self.hash_uint_size
- self.data_ptr = self.hashes[ZERO_FIELD]["pointer"]
-
- self.capacity_mask = int(self.table["capacity_mask"])
- self.capacity = (self.capacity_mask + 1) % self.modulo
-
- marker = self.table["marker"].type
- self.pair_type = marker.template_argument(0)
- self.pair_type_size = self.pair_type.sizeof
-
- self.valid_indices = []
- for idx in range(self.capacity):
- data_ptr = self.data_ptr.cast(self.hash_uint_type.pointer())
+ self._valobj = valobj
+ self._show_values = show_values
+
+ self._table = self._valobj["table"]
+ self._size = int(self._table["size"])
+ self._hashes = self._table["hashes"]
+ self._hash_uint_type = self._hashes.type
+ self._hash_uint_size = self._hashes.type.sizeof
+ self._modulo = 2 ** self._hash_uint_size
+ self._data_ptr = self._hashes[ZERO_FIELD]["pointer"]
+
+ self._capacity_mask = int(self._table["capacity_mask"])
+ self._capacity = (self._capacity_mask + 1) % self._modulo
+
+ marker = self._table["marker"].type
+ self._pair_type = marker.template_argument(0)
+ self._pair_type_size = self._pair_type.sizeof
+
+ self._valid_indices = []
+ for idx in range(self._capacity):
+ data_ptr = self._data_ptr.cast(self._hash_uint_type.pointer())
address = data_ptr + idx
hash_uint = address.dereference()
hash_ptr = hash_uint[ZERO_FIELD]["pointer"]
if int(hash_ptr) != 0:
- self.valid_indices.append(idx)
+ self._valid_indices.append(idx)
def to_string(self):
- if self.show_values:
- return "HashMap(size={})".format(self.size)
+ if self._show_values:
+ return "HashMap(size={})".format(self._size)
else:
- return "HashSet(size={})".format(self.size)
+ return "HashSet(size={})".format(self._size)
def children(self):
- start = int(self.data_ptr) & ~1
+ start = int(self._data_ptr) & ~1
- hashes = self.hash_uint_size * self.capacity
- align = self.pair_type_size
- len_rounded_up = (((((hashes + align) % self.modulo - 1) % self.modulo) & ~(
- (align - 1) % self.modulo)) % self.modulo - hashes) % self.modulo
+ hashes = self._hash_uint_size * self._capacity
+ align = self._pair_type_size
+ len_rounded_up = (((((hashes + align) % self._modulo - 1) % self._modulo) & ~(
+ (align - 1) % self._modulo)) % self._modulo - hashes) % self._modulo
pairs_offset = hashes + len_rounded_up
- pairs_start = gdb.Value(start + pairs_offset).cast(self.pair_type.pointer())
+ pairs_start = gdb.Value(start + pairs_offset).cast(self._pair_type.pointer())
- for index in range(self.size):
- table_index = self.valid_indices[index]
- idx = table_index & self.capacity_mask
+ for index in range(self._size):
+ table_index = self._valid_indices[index]
+ idx = table_index & self._capacity_mask
element = (pairs_start + idx).dereference()
- if self.show_values:
+ if self._show_values:
yield "key{}".format(index), element[ZERO_FIELD]
yield "val{}".format(index), element[FIRST_FIELD]
else:
yield "[{}]".format(index), element[ZERO_FIELD]
def display_hint(self):
- return "map" if self.show_values else "array"
+ return "map" if self._show_values else "array"
-class StdHashMapProvider:
+class StdHashMapProvider(printer_base):
def __init__(self, valobj, show_values=True):
- self.valobj = valobj
- self.show_values = show_values
+ self._valobj = valobj
+ self._show_values = show_values
- table = self.table()
+ table = self._table()
table_inner = table["table"]
capacity = int(table_inner["bucket_mask"]) + 1
ctrl = table_inner["ctrl"]["pointer"]
- self.size = int(table_inner["items"])
- self.pair_type = table.type.template_argument(0).strip_typedefs()
+ self._size = int(table_inner["items"])
+ self._pair_type = table.type.template_argument(0).strip_typedefs()
- self.new_layout = not table_inner.type.has_key("data")
- if self.new_layout:
- self.data_ptr = ctrl.cast(self.pair_type.pointer())
+ self._new_layout = not table_inner.type.has_key("data")
+ if self._new_layout:
+ self._data_ptr = ctrl.cast(self._pair_type.pointer())
else:
- self.data_ptr = table_inner["data"]["pointer"]
+ self._data_ptr = table_inner["data"]["pointer"]
- self.valid_indices = []
+ self._valid_indices = []
for idx in range(capacity):
address = ctrl + idx
value = address.dereference()
is_presented = value & 128 == 0
if is_presented:
- self.valid_indices.append(idx)
+ self._valid_indices.append(idx)
- def table(self):
- if self.show_values:
- hashbrown_hashmap = self.valobj["base"]
- elif self.valobj.type.fields()[0].name == "map":
+ def _table(self):
+ if self._show_values:
+ hashbrown_hashmap = self._valobj["base"]
+ elif self._valobj.type.fields()[0].name == "map":
# BACKCOMPAT: rust 1.47
# HashSet wraps std::collections::HashMap, which wraps hashbrown::HashMap
- hashbrown_hashmap = self.valobj["map"]["base"]
+ hashbrown_hashmap = self._valobj["map"]["base"]
else:
# HashSet wraps hashbrown::HashSet, which wraps hashbrown::HashMap
- hashbrown_hashmap = self.valobj["base"]["map"]
+ hashbrown_hashmap = self._valobj["base"]["map"]
return hashbrown_hashmap["table"]
def to_string(self):
- if self.show_values:
- return "HashMap(size={})".format(self.size)
+ if self._show_values:
+ return "HashMap(size={})".format(self._size)
else:
- return "HashSet(size={})".format(self.size)
+ return "HashSet(size={})".format(self._size)
def children(self):
- pairs_start = self.data_ptr
+ pairs_start = self._data_ptr
- for index in range(self.size):
- idx = self.valid_indices[index]
- if self.new_layout:
+ for index in range(self._size):
+ idx = self._valid_indices[index]
+ if self._new_layout:
idx = -(idx + 1)
element = (pairs_start + idx).dereference()
- if self.show_values:
+ if self._show_values:
yield "key{}".format(index), element[ZERO_FIELD]
yield "val{}".format(index), element[FIRST_FIELD]
else:
yield "[{}]".format(index), element[ZERO_FIELD]
def display_hint(self):
- return "map" if self.show_values else "array"
+ return "map" if self._show_values else "array"
diff --git a/src/etc/test-float-parse/Cargo.lock b/src/etc/test-float-parse/Cargo.lock
new file mode 100644
index 000000000..3f60423fe
--- /dev/null
+++ b/src/etc/test-float-parse/Cargo.lock
@@ -0,0 +1,75 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "getrandom"
+version = "0.2.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "wasi",
+]
+
+[[package]]
+name = "libc"
+version = "0.2.147"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3"
+
+[[package]]
+name = "ppv-lite86"
+version = "0.2.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
+
+[[package]]
+name = "rand"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
+dependencies = [
+ "libc",
+ "rand_chacha",
+ "rand_core",
+]
+
+[[package]]
+name = "rand_chacha"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
+dependencies = [
+ "ppv-lite86",
+ "rand_core",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
+dependencies = [
+ "getrandom",
+]
+
+[[package]]
+name = "test-float-parse"
+version = "0.1.0"
+dependencies = [
+ "rand",
+]
+
+[[package]]
+name = "wasi"
+version = "0.11.0+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
diff --git a/src/etc/test-float-parse/Cargo.toml b/src/etc/test-float-parse/Cargo.toml
index 6d7b227d0..a045be956 100644
--- a/src/etc/test-float-parse/Cargo.toml
+++ b/src/etc/test-float-parse/Cargo.toml
@@ -8,7 +8,7 @@ publish = false
resolver = "1"
[dependencies]
-rand = "0.4"
+rand = "0.8"
[lib]
name = "test_float_parse"
diff --git a/src/librustdoc/Cargo.toml b/src/librustdoc/Cargo.toml
index 29912b957..f3917b978 100644
--- a/src/librustdoc/Cargo.toml
+++ b/src/librustdoc/Cargo.toml
@@ -10,7 +10,8 @@ path = "lib.rs"
arrayvec = { version = "0.7", default-features = false }
askama = { version = "0.12", default-features = false, features = ["config"] }
itertools = "0.10.1"
-minifier = "0.2.2"
+indexmap = "2"
+minifier = "0.3.0"
once_cell = "1.10.0"
regex = "1"
rustdoc-json-types = { path = "../rustdoc-json-types" }
diff --git a/src/librustdoc/clean/auto_trait.rs b/src/librustdoc/clean/auto_trait.rs
index a06f31a93..bdf6a0f6b 100644
--- a/src/librustdoc/clean/auto_trait.rs
+++ b/src/librustdoc/clean/auto_trait.rs
@@ -551,8 +551,8 @@ where
WherePredicate::RegionPredicate { lifetime, bounds } => {
lifetime_to_bounds.entry(lifetime).or_default().extend(bounds);
}
- WherePredicate::EqPredicate { lhs, rhs, bound_params } => {
- match *lhs {
+ WherePredicate::EqPredicate { lhs, rhs } => {
+ match lhs {
Type::QPath(box QPathData {
ref assoc,
ref self_type,
@@ -590,14 +590,13 @@ where
GenericArgs::AngleBracketed { ref mut bindings, .. } => {
bindings.push(TypeBinding {
assoc: assoc.clone(),
- kind: TypeBindingKind::Equality { term: *rhs },
+ kind: TypeBindingKind::Equality { term: rhs },
});
}
GenericArgs::Parenthesized { .. } => {
existing_predicates.push(WherePredicate::EqPredicate {
lhs: lhs.clone(),
rhs,
- bound_params,
});
continue; // If something other than a Fn ends up
// with parentheses, leave it alone
diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs
index fcbcfbf5c..974ba1e3b 100644
--- a/src/librustdoc/clean/inline.rs
+++ b/src/librustdoc/clean/inline.rs
@@ -18,14 +18,16 @@ use rustc_span::hygiene::MacroKind;
use rustc_span::symbol::{kw, sym, Symbol};
use crate::clean::{
- self, clean_fn_decl_from_did_and_sig, clean_generics, clean_impl_item, clean_middle_assoc_item,
- clean_middle_field, clean_middle_ty, clean_trait_ref_with_bindings, clean_ty,
- clean_ty_alias_inner_type, clean_ty_generics, clean_variant_def, utils, Attributes,
+ self, clean_bound_vars, clean_fn_decl_from_did_and_sig, clean_generics, clean_impl_item,
+ clean_middle_assoc_item, clean_middle_field, clean_middle_ty, clean_trait_ref_with_bindings,
+ clean_ty, clean_ty_alias_inner_type, clean_ty_generics, clean_variant_def, utils, Attributes,
AttributesExt, ImplKind, ItemId, Type,
};
use crate::core::DocContext;
use crate::formats::item_type::ItemType;
+use super::Item;
+
/// Attempt to inline a definition into this AST.
///
/// This function will fetch the definition specified, and if it is
@@ -83,7 +85,7 @@ pub(crate) fn try_inline(
Res::Def(DefKind::TyAlias, did) => {
record_extern_fqn(cx, did, ItemType::TypeAlias);
build_impls(cx, did, attrs_without_docs, &mut ret);
- clean::TypeAliasItem(build_type_alias(cx, did))
+ clean::TypeAliasItem(build_type_alias(cx, did, &mut ret))
}
Res::Def(DefKind::Enum, did) => {
record_extern_fqn(cx, did, ItemType::Enum);
@@ -239,20 +241,13 @@ pub(crate) fn build_external_trait(cx: &mut DocContext<'_>, did: DefId) -> clean
fn build_external_function<'tcx>(cx: &mut DocContext<'tcx>, did: DefId) -> Box<clean::Function> {
let sig = cx.tcx.fn_sig(did).instantiate_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 => {
- Some(clean::GenericParamDef::lifetime(name))
- }
- _ => None,
- });
-
let predicates = cx.tcx.explicit_predicates_of(did);
+
let (generics, decl) = clean::enter_impl_trait(cx, |cx| {
// NOTE: generics need to be cleaned before the decl!
let mut generics = clean_ty_generics(cx, cx.tcx.generics_of(did), predicates);
// FIXME: This does not place parameters in source order (late-bound ones come last)
- generics.params.extend(late_bound_regions);
+ generics.params.extend(clean_bound_vars(sig.bound_vars()));
let decl = clean_fn_decl_from_did_and_sig(cx, Some(did), sig);
(generics, decl)
});
@@ -288,11 +283,15 @@ fn build_union(cx: &mut DocContext<'_>, did: DefId) -> clean::Union {
clean::Union { generics, fields }
}
-fn build_type_alias(cx: &mut DocContext<'_>, did: DefId) -> Box<clean::TypeAlias> {
+fn build_type_alias(
+ cx: &mut DocContext<'_>,
+ did: DefId,
+ ret: &mut Vec<Item>,
+) -> Box<clean::TypeAlias> {
let predicates = cx.tcx.explicit_predicates_of(did);
let ty = cx.tcx.type_of(did).instantiate_identity();
let type_ = clean_middle_ty(ty::Binder::dummy(ty), cx, Some(did), None);
- let inner_type = clean_ty_alias_inner_type(ty, cx);
+ let inner_type = clean_ty_alias_inner_type(ty, cx, ret);
Box::new(clean::TypeAlias {
type_,
@@ -600,7 +599,7 @@ fn build_module_items(
let prim_ty = clean::PrimitiveType::from(p);
items.push(clean::Item {
name: None,
- attrs: Box::new(clean::Attributes::default()),
+ attrs: Box::default(),
// We can use the item's `DefId` directly since the only information ever used
// from it is `DefId.krate`.
item_id: ItemId::DefId(did),
@@ -648,13 +647,13 @@ fn build_const(cx: &mut DocContext<'_>, def_id: DefId) -> clean::Constant {
clean::simplify::move_bounds_to_generic_parameters(&mut generics);
clean::Constant {
- type_: clean_middle_ty(
+ type_: Box::new(clean_middle_ty(
ty::Binder::dummy(cx.tcx.type_of(def_id).instantiate_identity()),
cx,
Some(def_id),
None,
- ),
- generics: Box::new(generics),
+ )),
+ generics,
kind: clean::ConstantKind::Extern { def_id },
}
}
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index 0caa92e44..1b7ca7bf7 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -232,20 +232,11 @@ fn clean_poly_trait_ref_with_bindings<'tcx>(
poly_trait_ref: ty::PolyTraitRef<'tcx>,
bindings: ThinVec<TypeBinding>,
) -> GenericBound {
- // collect any late bound regions
- let late_bound_regions: Vec<_> = cx
- .tcx
- .collect_referenced_late_bound_regions(&poly_trait_ref)
- .into_iter()
- .filter_map(|br| match br {
- ty::BrNamed(_, name) if br.is_named() => Some(GenericParamDef::lifetime(name)),
- _ => None,
- })
- .collect();
-
- let trait_ = clean_trait_ref_with_bindings(cx, poly_trait_ref, bindings);
GenericBound::TraitBound(
- PolyTrait { trait_, generic_params: late_bound_regions },
+ PolyTrait {
+ trait_: clean_trait_ref_with_bindings(cx, poly_trait_ref, bindings),
+ generic_params: clean_bound_vars(poly_trait_ref.bound_vars()),
+ },
hir::TraitBoundModifier::None,
)
}
@@ -268,13 +259,13 @@ 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(
+ type_: Box::new(clean_middle_ty(
ty::Binder::dummy(cx.tcx.type_of(def_id).instantiate_identity()),
cx,
Some(def_id),
None,
- ),
- generics: Box::new(Generics::default()),
+ )),
+ generics: Generics::default(),
kind: ConstantKind::Anonymous { body: constant.value.body },
}
}
@@ -285,8 +276,8 @@ pub(crate) fn clean_middle_const<'tcx>(
) -> Constant {
// FIXME: instead of storing the stringified expression, store `self` directly instead.
Constant {
- type_: clean_middle_ty(constant.map_bound(|c| c.ty()), cx, None, None),
- generics: Box::new(Generics::default()),
+ type_: Box::new(clean_middle_ty(constant.map_bound(|c| c.ty()), cx, None, None)),
+ generics: Generics::default(),
kind: ConstantKind::TyConst { expr: constant.skip_binder().to_string().into() },
}
}
@@ -338,9 +329,8 @@ fn clean_where_predicate<'tcx>(
},
hir::WherePredicate::EqPredicate(ref wrp) => WherePredicate::EqPredicate {
- lhs: Box::new(clean_ty(wrp.lhs_ty, cx)),
- rhs: Box::new(clean_ty(wrp.rhs_ty, cx).into()),
- bound_params: Vec::new(),
+ lhs: clean_ty(wrp.lhs_ty, cx),
+ rhs: clean_ty(wrp.rhs_ty, cx).into(),
},
})
}
@@ -436,20 +426,9 @@ fn clean_projection_predicate<'tcx>(
pred: ty::Binder<'tcx, ty::ProjectionPredicate<'tcx>>,
cx: &mut DocContext<'tcx>,
) -> WherePredicate {
- let late_bound_regions = cx
- .tcx
- .collect_referenced_late_bound_regions(&pred)
- .into_iter()
- .filter_map(|br| match br {
- ty::BrNamed(_, name) if br.is_named() => Some(GenericParamDef::lifetime(name)),
- _ => None,
- })
- .collect();
-
WherePredicate::EqPredicate {
- lhs: Box::new(clean_projection(pred.map_bound(|p| p.projection_ty), cx, None)),
- rhs: Box::new(clean_middle_term(pred.map_bound(|p| p.term), cx)),
- bound_params: late_bound_regions,
+ lhs: clean_projection(pred.map_bound(|p| p.projection_ty), cx, None),
+ rhs: clean_middle_term(pred.map_bound(|p| p.term), cx),
}
}
@@ -496,8 +475,9 @@ fn projection_to_path_segment<'tcx>(
ty: ty::Binder<'tcx, ty::AliasTy<'tcx>>,
cx: &mut DocContext<'tcx>,
) -> PathSegment {
- let item = cx.tcx.associated_item(ty.skip_binder().def_id);
- let generics = cx.tcx.generics_of(ty.skip_binder().def_id);
+ let def_id = ty.skip_binder().def_id;
+ let item = cx.tcx.associated_item(def_id);
+ let generics = cx.tcx.generics_of(def_id);
PathSegment {
name: item.name,
args: GenericArgs::AngleBracketed {
@@ -505,7 +485,7 @@ fn projection_to_path_segment<'tcx>(
cx,
ty.map_bound(|ty| &ty.args[generics.parent_count..]),
false,
- None,
+ def_id,
)
.into(),
bindings: Default::default(),
@@ -519,7 +499,7 @@ fn clean_generic_param_def<'tcx>(
) -> GenericParamDef {
let (name, kind) = match def.kind {
ty::GenericParamDefKind::Lifetime => {
- (def.name, GenericParamDefKind::Lifetime { outlives: vec![] })
+ (def.name, GenericParamDefKind::Lifetime { outlives: ThinVec::new() })
}
ty::GenericParamDefKind::Type { has_default, synthetic, .. } => {
let default = if has_default {
@@ -536,7 +516,7 @@ fn clean_generic_param_def<'tcx>(
def.name,
GenericParamDefKind::Type {
did: def.def_id,
- bounds: vec![], // These are filled in from the where-clauses.
+ bounds: ThinVec::new(), // These are filled in from the where-clauses.
default: default.map(Box::new),
synthetic,
},
@@ -588,7 +568,7 @@ fn clean_generic_param<'tcx>(
})
.collect()
} else {
- Vec::new()
+ ThinVec::new()
};
(param.name.ident().name, GenericParamDefKind::Lifetime { outlives })
}
@@ -601,7 +581,7 @@ fn clean_generic_param<'tcx>(
.filter_map(|x| clean_generic_bound(x, cx))
.collect()
} else {
- Vec::new()
+ ThinVec::new()
};
(
param.name.ident().name,
@@ -657,7 +637,7 @@ pub(crate) fn clean_generics<'tcx>(
match param.kind {
GenericParamDefKind::Lifetime { .. } => unreachable!(),
GenericParamDefKind::Type { did, ref bounds, .. } => {
- cx.impl_trait_bounds.insert(did.into(), bounds.clone());
+ cx.impl_trait_bounds.insert(did.into(), bounds.to_vec());
}
GenericParamDefKind::Const { .. } => unreachable!(),
}
@@ -705,8 +685,8 @@ pub(crate) fn clean_generics<'tcx>(
}
}
}
- WherePredicate::EqPredicate { lhs, rhs, bound_params } => {
- eq_predicates.push(WherePredicate::EqPredicate { lhs, rhs, bound_params });
+ WherePredicate::EqPredicate { lhs, rhs } => {
+ eq_predicates.push(WherePredicate::EqPredicate { lhs, rhs });
}
}
}
@@ -800,11 +780,9 @@ fn clean_ty_generics<'tcx>(
})
.collect::<ThinVec<GenericParamDef>>();
- // param index -> [(trait DefId, associated type name & generics, term, higher-ranked params)]
- let mut impl_trait_proj = FxHashMap::<
- u32,
- Vec<(DefId, PathSegment, ty::Binder<'_, ty::Term<'_>>, Vec<GenericParamDef>)>,
- >::default();
+ // param index -> [(trait DefId, associated type name & generics, term)]
+ let mut impl_trait_proj =
+ FxHashMap::<u32, Vec<(DefId, PathSegment, ty::Binder<'_, ty::Term<'_>>)>>::default();
let where_predicates = preds
.predicates
@@ -856,11 +834,6 @@ fn clean_ty_generics<'tcx>(
trait_did,
name,
proj.map_bound(|p| p.term),
- pred.get_bound_params()
- .into_iter()
- .flatten()
- .cloned()
- .collect(),
));
}
@@ -896,9 +869,9 @@ fn clean_ty_generics<'tcx>(
let crate::core::ImplTraitParam::ParamIndex(idx) = param else { unreachable!() };
if let Some(proj) = impl_trait_proj.remove(&idx) {
- for (trait_did, name, rhs, bound_params) in proj {
+ for (trait_did, name, rhs) in proj {
let rhs = clean_middle_term(rhs, cx);
- simplify::merge_bounds(cx, &mut bounds, bound_params, trait_did, name, &rhs);
+ simplify::merge_bounds(cx, &mut bounds, trait_did, name, &rhs);
}
}
@@ -962,11 +935,16 @@ fn clean_ty_generics<'tcx>(
fn clean_ty_alias_inner_type<'tcx>(
ty: Ty<'tcx>,
cx: &mut DocContext<'tcx>,
+ ret: &mut Vec<Item>,
) -> Option<TypeAliasInnerType> {
let ty::Adt(adt_def, args) = ty.kind() else {
return None;
};
+ if !adt_def.did().is_local() {
+ inline::build_impls(cx, adt_def.did(), None, ret);
+ }
+
Some(if adt_def.is_enum() {
let variants: rustc_index::IndexVec<_, _> = adt_def
.variants()
@@ -974,6 +952,10 @@ fn clean_ty_alias_inner_type<'tcx>(
.map(|variant| clean_variant_def_with_args(variant, args, cx))
.collect();
+ if !adt_def.did().is_local() {
+ inline::record_extern_fqn(cx, adt_def.did(), ItemType::Enum);
+ }
+
TypeAliasInnerType::Enum {
variants,
is_non_exhaustive: adt_def.is_variant_list_non_exhaustive(),
@@ -989,8 +971,14 @@ fn clean_ty_alias_inner_type<'tcx>(
clean_variant_def_with_args(variant, args, cx).kind.inner_items().cloned().collect();
if adt_def.is_struct() {
+ if !adt_def.did().is_local() {
+ inline::record_extern_fqn(cx, adt_def.did(), ItemType::Struct);
+ }
TypeAliasInnerType::Struct { ctor_kind: variant.ctor_kind(), fields }
} else {
+ if !adt_def.did().is_local() {
+ inline::record_extern_fqn(cx, adt_def.did(), ItemType::Union);
+ }
TypeAliasInnerType::Union { fields }
}
})
@@ -1244,14 +1232,14 @@ fn clean_trait_item<'tcx>(trait_item: &hir::TraitItem<'tcx>, cx: &mut DocContext
hir::TraitItemKind::Const(ty, Some(default)) => {
let generics = enter_impl_trait(cx, |cx| clean_generics(trait_item.generics, cx));
AssocConstItem(
- Box::new(generics),
- clean_ty(ty, cx),
+ generics,
+ Box::new(clean_ty(ty, cx)),
ConstantKind::Local { def_id: local_did, body: default },
)
}
hir::TraitItemKind::Const(ty, None) => {
let generics = enter_impl_trait(cx, |cx| clean_generics(trait_item.generics, cx));
- TyAssocConstItem(Box::new(generics), clean_ty(ty, cx))
+ TyAssocConstItem(generics, Box::new(clean_ty(ty, cx)))
}
hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Provided(body)) => {
let m = clean_function(cx, sig, trait_item.generics, FunctionArgs::Body(body));
@@ -1300,7 +1288,7 @@ pub(crate) fn clean_impl_item<'tcx>(
hir::ImplItemKind::Const(ty, expr) => {
let generics = clean_generics(impl_.generics, cx);
let default = ConstantKind::Local { def_id: local_did, body: expr };
- AssocConstItem(Box::new(generics), clean_ty(ty, cx), default)
+ AssocConstItem(generics, Box::new(clean_ty(ty, cx)), default)
}
hir::ImplItemKind::Fn(ref sig, body) => {
let m = clean_function(cx, sig, impl_.generics, FunctionArgs::Body(body));
@@ -1339,18 +1327,18 @@ pub(crate) fn clean_middle_assoc_item<'tcx>(
let tcx = cx.tcx;
let kind = match assoc_item.kind {
ty::AssocKind::Const => {
- let ty = clean_middle_ty(
+ let ty = Box::new(clean_middle_ty(
ty::Binder::dummy(tcx.type_of(assoc_item.def_id).instantiate_identity()),
cx,
Some(assoc_item.def_id),
None,
- );
+ ));
- let mut generics = Box::new(clean_ty_generics(
+ let mut generics = clean_ty_generics(
cx,
tcx.generics_of(assoc_item.def_id),
tcx.explicit_predicates_of(assoc_item.def_id),
- ));
+ );
simplify::move_bounds_to_generic_parameters(&mut generics);
let provided = match assoc_item.container {
@@ -1365,23 +1353,13 @@ pub(crate) fn clean_middle_assoc_item<'tcx>(
}
ty::AssocKind::Fn => {
let sig = tcx.fn_sig(assoc_item.def_id).instantiate_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 =>
- {
- Some(GenericParamDef::lifetime(name))
- }
- _ => None,
- });
-
let mut generics = clean_ty_generics(
cx,
tcx.generics_of(assoc_item.def_id),
tcx.explicit_predicates_of(assoc_item.def_id),
);
// FIXME: This does not place parameters in source order (late-bound ones come last)
- generics.params.extend(late_bound_regions);
+ generics.params.extend(clean_bound_vars(sig.bound_vars()));
let mut decl = clean_fn_decl_from_did_and_sig(cx, Some(assoc_item.def_id), sig);
@@ -2117,9 +2095,11 @@ pub(crate) fn clean_middle_ty<'tcx>(
// FIXME: should we merge the outer and inner binders somehow?
let sig = bound_ty.skip_binder().fn_sig(cx.tcx);
let decl = clean_fn_decl_from_did_and_sig(cx, None, sig);
+ let generic_params = clean_bound_vars(sig.bound_vars());
+
BareFunction(Box::new(BareFunctionDecl {
unsafety: sig.unsafety(),
- generic_params: Vec::new(),
+ generic_params,
decl,
abi: sig.abi(),
}))
@@ -2195,8 +2175,8 @@ pub(crate) fn clean_middle_ty<'tcx>(
let late_bound_regions: FxIndexSet<_> = obj
.iter()
- .flat_map(|pb| pb.bound_vars())
- .filter_map(|br| match br {
+ .flat_map(|pred| pred.bound_vars())
+ .filter_map(|var| match var {
ty::BoundVariableKind::Region(ty::BrNamed(_, name))
if name != kw::UnderscoreLifetime =>
{
@@ -2221,18 +2201,19 @@ pub(crate) fn clean_middle_ty<'tcx>(
}
ty::Alias(ty::Inherent, alias_ty) => {
+ let def_id = alias_ty.def_id;
let alias_ty = bound_ty.rebind(alias_ty);
let self_type = clean_middle_ty(alias_ty.map_bound(|ty| ty.self_ty()), cx, None, None);
Type::QPath(Box::new(QPathData {
assoc: PathSegment {
- name: cx.tcx.associated_item(alias_ty.skip_binder().def_id).name,
+ name: cx.tcx.associated_item(def_id).name,
args: GenericArgs::AngleBracketed {
args: ty_args_to_args(
cx,
alias_ty.map_bound(|ty| ty.args.as_slice()),
true,
- None,
+ def_id,
)
.into(),
bindings: Default::default(),
@@ -2270,6 +2251,11 @@ pub(crate) fn clean_middle_ty<'tcx>(
}
}
+ ty::Bound(_, ref ty) => match ty.kind {
+ ty::BoundTyKind::Param(_, name) => Generic(name),
+ ty::BoundTyKind::Anon => panic!("unexpected anonymous bound type variable"),
+ },
+
ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) => {
// If it's already in the same alias, don't get an infinite loop.
if cx.current_type_aliases.contains_key(&def_id) {
@@ -2297,10 +2283,9 @@ pub(crate) fn clean_middle_ty<'tcx>(
}
ty::Closure(..) => panic!("Closure"),
- ty::Generator(..) => panic!("Generator"),
- ty::Bound(..) => panic!("Bound"),
+ ty::Coroutine(..) => panic!("Coroutine"),
ty::Placeholder(..) => panic!("Placeholder"),
- ty::GeneratorWitness(..) => panic!("GeneratorWitness"),
+ ty::CoroutineWitness(..) => panic!("CoroutineWitness"),
ty::Infer(..) => panic!("Infer"),
ty::Error(_) => rustc_errors::FatalError.raise(),
}
@@ -2549,7 +2534,8 @@ fn clean_generic_args<'tcx>(
}
hir::GenericArg::Lifetime(_) => GenericArg::Lifetime(Lifetime::elided()),
hir::GenericArg::Type(ty) => GenericArg::Type(clean_ty(ty, cx)),
- // FIXME(effects): This will still emit `<true>` for non-const impls of const traits
+ // Checking for `#[rustc_host]` on the `AnonConst` not only accounts for the case
+ // where the argument is `host` but for all possible cases (e.g., `true`, `false`).
hir::GenericArg::Const(ct)
if cx.tcx.has_attr(ct.value.def_id, sym::rustc_host) =>
{
@@ -2750,8 +2736,8 @@ fn clean_maybe_renamed_item<'tcx>(
StaticItem(Static { type_: clean_ty(ty, cx), mutability, expr: Some(body_id) })
}
ItemKind::Const(ty, generics, body_id) => ConstantItem(Constant {
- type_: clean_ty(ty, cx),
- generics: Box::new(clean_generics(generics, cx)),
+ type_: Box::new(clean_ty(ty, cx)),
+ generics: clean_generics(generics, cx),
kind: ConstantKind::Local { body: body_id, def_id },
}),
ItemKind::OpaqueTy(ref ty) => OpaqueTyItem(OpaqueTy {
@@ -2776,14 +2762,24 @@ fn clean_maybe_renamed_item<'tcx>(
}
let ty = cx.tcx.type_of(def_id).instantiate_identity();
- let inner_type = clean_ty_alias_inner_type(ty, cx);
- TypeAliasItem(Box::new(TypeAlias {
- generics,
- inner_type,
- type_: rustdoc_ty,
- item_type: Some(type_),
- }))
+ let mut ret = Vec::new();
+ let inner_type = clean_ty_alias_inner_type(ty, cx, &mut ret);
+
+ ret.push(generate_item_with_correct_attrs(
+ cx,
+ TypeAliasItem(Box::new(TypeAlias {
+ generics,
+ inner_type,
+ type_: rustdoc_ty,
+ item_type: Some(type_),
+ })),
+ item.owner_id.def_id.to_def_id(),
+ name,
+ import_id,
+ renamed,
+ ));
+ return ret;
}
ItemKind::Enum(ref def, generics) => EnumItem(Enum {
variants: def.variants.iter().map(|v| clean_variant(v, cx)).collect(),
@@ -3137,3 +3133,30 @@ fn clean_type_binding<'tcx>(
},
}
}
+
+fn clean_bound_vars<'tcx>(
+ bound_vars: &'tcx ty::List<ty::BoundVariableKind>,
+) -> Vec<GenericParamDef> {
+ bound_vars
+ .into_iter()
+ .filter_map(|var| match var {
+ ty::BoundVariableKind::Region(ty::BrNamed(_, name))
+ if name != kw::UnderscoreLifetime =>
+ {
+ Some(GenericParamDef::lifetime(name))
+ }
+ ty::BoundVariableKind::Ty(ty::BoundTyKind::Param(did, name)) => Some(GenericParamDef {
+ name,
+ kind: GenericParamDefKind::Type {
+ did,
+ bounds: ThinVec::new(),
+ default: None,
+ synthetic: false,
+ },
+ }),
+ // FIXME(non_lifetime_binders): Support higher-ranked const parameters.
+ ty::BoundVariableKind::Const => None,
+ _ => None,
+ })
+ .collect()
+}
diff --git a/src/librustdoc/clean/simplify.rs b/src/librustdoc/clean/simplify.rs
index 7b8f20326..627f15e67 100644
--- a/src/librustdoc/clean/simplify.rs
+++ b/src/librustdoc/clean/simplify.rs
@@ -40,18 +40,18 @@ pub(crate) fn where_clauses(cx: &DocContext<'_>, clauses: Vec<WP>) -> ThinVec<WP
WP::RegionPredicate { lifetime, bounds } => {
lifetimes.push((lifetime, bounds));
}
- WP::EqPredicate { lhs, rhs, bound_params } => equalities.push((lhs, rhs, bound_params)),
+ WP::EqPredicate { lhs, rhs } => equalities.push((lhs, rhs)),
}
}
// Look for equality predicates on associated types that can be merged into
// general bound predicates.
- equalities.retain(|(lhs, rhs, bound_params)| {
+ equalities.retain(|(lhs, rhs)| {
let Some((ty, trait_did, name)) = lhs.projection() else {
return true;
};
let Some((bounds, _)) = tybounds.get_mut(ty) else { return true };
- merge_bounds(cx, bounds, bound_params.clone(), trait_did, name, rhs)
+ merge_bounds(cx, bounds, trait_did, name, rhs)
});
// And finally, let's reassemble everything
@@ -64,18 +64,13 @@ pub(crate) fn where_clauses(cx: &DocContext<'_>, clauses: Vec<WP>) -> ThinVec<WP
bounds,
bound_params,
}));
- clauses.extend(equalities.into_iter().map(|(lhs, rhs, bound_params)| WP::EqPredicate {
- lhs,
- rhs,
- bound_params,
- }));
+ clauses.extend(equalities.into_iter().map(|(lhs, rhs)| WP::EqPredicate { lhs, rhs }));
clauses
}
pub(crate) fn merge_bounds(
cx: &clean::DocContext<'_>,
bounds: &mut Vec<clean::GenericBound>,
- mut bound_params: Vec<clean::GenericParamDef>,
trait_did: DefId,
assoc: clean::PathSegment,
rhs: &clean::Term,
@@ -93,12 +88,6 @@ pub(crate) fn merge_bounds(
}
let last = trait_ref.trait_.segments.last_mut().expect("segments were empty");
- trait_ref.generic_params.append(&mut bound_params);
- // Sort parameters (likely) originating from a hashset alphabetically to
- // produce predictable output (and to allow for full deduplication).
- trait_ref.generic_params.sort_unstable_by(|p, q| p.name.as_str().cmp(q.name.as_str()));
- trait_ref.generic_params.dedup_by_key(|p| p.name);
-
match last.args {
PP::AngleBracketed { ref mut bindings, .. } => {
bindings.push(clean::TypeBinding {
@@ -156,7 +145,7 @@ pub(crate) fn move_bounds_to_generic_parameters(generics: &mut clean::Generics)
..
}) = generics.params.iter_mut().find(|param| &param.name == arg)
{
- param_bounds.append(bounds);
+ param_bounds.extend(bounds.drain(..));
} else if let WherePredicate::RegionPredicate { lifetime: Lifetime(arg), bounds } = &mut pred
&& let Some(GenericParamDef {
kind: GenericParamDefKind::Lifetime { outlives: param_bounds },
diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs
index 4256e7b51..88ee4e3a2 100644
--- a/src/librustdoc/clean/types.rs
+++ b/src/librustdoc/clean/types.rs
@@ -12,7 +12,7 @@ use thin_vec::ThinVec;
use rustc_ast as ast;
use rustc_ast_pretty::pprust;
-use rustc_attr::{ConstStability, Deprecation, Stability, StabilityLevel};
+use rustc_attr::{ConstStability, Deprecation, Stability, StabilityLevel, StableSince};
use rustc_const_eval::const_eval::is_unstable_const_fn;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_hir as hir;
@@ -585,14 +585,14 @@ impl Item {
})
}
- pub(crate) fn stable_since(&self, tcx: TyCtxt<'_>) -> Option<Symbol> {
+ pub(crate) fn stable_since(&self, tcx: TyCtxt<'_>) -> Option<StableSince> {
match self.stability(tcx)?.level {
StabilityLevel::Stable { since, .. } => Some(since),
StabilityLevel::Unstable { .. } => None,
}
}
- pub(crate) fn const_stable_since(&self, tcx: TyCtxt<'_>) -> Option<Symbol> {
+ pub(crate) fn const_stable_since(&self, tcx: TyCtxt<'_>) -> Option<StableSince> {
match self.const_stability(tcx)?.level {
StabilityLevel::Stable { since, .. } => Some(since),
StabilityLevel::Unstable { .. } => None,
@@ -713,12 +713,16 @@ impl Item {
Some(tcx.visibility(def_id))
}
- pub(crate) fn attributes(&self, tcx: TyCtxt<'_>, keep_as_is: bool) -> Vec<String> {
+ pub(crate) fn attributes(
+ &self,
+ tcx: TyCtxt<'_>,
+ cache: &Cache,
+ keep_as_is: bool,
+ ) -> Vec<String> {
const ALLOWED_ATTRIBUTES: &[Symbol] =
- &[sym::export_name, sym::link_section, sym::no_mangle, sym::repr, sym::non_exhaustive];
+ &[sym::export_name, sym::link_section, sym::no_mangle, sym::non_exhaustive];
use rustc_abi::IntegerType;
- use rustc_middle::ty::ReprFlags;
let mut attrs: Vec<String> = self
.attrs
@@ -739,20 +743,38 @@ impl Item {
}
})
.collect();
- if let Some(def_id) = self.def_id() &&
- !def_id.is_local() &&
- // This check is needed because `adt_def` will panic if not a compatible type otherwise...
- matches!(self.type_(), ItemType::Struct | ItemType::Enum | ItemType::Union)
+ if !keep_as_is
+ && let Some(def_id) = self.def_id()
+ && let ItemType::Struct | ItemType::Enum | ItemType::Union = self.type_()
{
- let repr = tcx.adt_def(def_id).repr();
+ let adt = tcx.adt_def(def_id);
+ let repr = adt.repr();
let mut out = Vec::new();
- if repr.flags.contains(ReprFlags::IS_C) {
+ if repr.c() {
out.push("C");
}
- if repr.flags.contains(ReprFlags::IS_TRANSPARENT) {
- out.push("transparent");
+ if repr.transparent() {
+ // Render `repr(transparent)` iff the non-1-ZST field is public or at least one
+ // field is public in case all fields are 1-ZST fields.
+ let render_transparent = cache.document_private
+ || adt
+ .all_fields()
+ .find(|field| {
+ let ty =
+ field.ty(tcx, ty::GenericArgs::identity_for_item(tcx, field.did));
+ tcx.layout_of(tcx.param_env(field.did).and(ty))
+ .is_ok_and(|layout| !layout.is_1zst())
+ })
+ .map_or_else(
+ || adt.all_fields().any(|field| field.vis.is_public()),
+ |field| field.vis.is_public(),
+ );
+
+ if render_transparent {
+ out.push("transparent");
+ }
}
- if repr.flags.contains(ReprFlags::IS_SIMD) {
+ if repr.simd() {
out.push("simd");
}
let pack_s;
@@ -777,10 +799,9 @@ impl Item {
};
out.push(&int_s);
}
- if out.is_empty() {
- return Vec::new();
+ if !out.is_empty() {
+ attrs.push(format!("#[repr({})]", out.join(", ")));
}
- attrs.push(format!("#[repr({})]", out.join(", ")));
}
attrs
}
@@ -831,9 +852,9 @@ pub(crate) enum ItemKind {
ProcMacroItem(ProcMacro),
PrimitiveItem(PrimitiveType),
/// A required associated constant in a trait declaration.
- TyAssocConstItem(Box<Generics>, Type),
+ TyAssocConstItem(Generics, Box<Type>),
/// An associated constant in a trait impl or a provided one in a trait declaration.
- AssocConstItem(Box<Generics>, Type, ConstantKind),
+ AssocConstItem(Generics, Box<Type>, ConstantKind),
/// A required associated type in a trait declaration.
///
/// The bounds may be non-empty if there is a `where` clause.
@@ -1289,7 +1310,7 @@ impl Lifetime {
pub(crate) enum WherePredicate {
BoundPredicate { ty: Type, bounds: Vec<GenericBound>, bound_params: Vec<GenericParamDef> },
RegionPredicate { lifetime: Lifetime, bounds: Vec<GenericBound> },
- EqPredicate { lhs: Box<Type>, rhs: Box<Term>, bound_params: Vec<GenericParamDef> },
+ EqPredicate { lhs: Type, rhs: Term },
}
impl WherePredicate {
@@ -1300,21 +1321,13 @@ impl WherePredicate {
_ => None,
}
}
-
- pub(crate) fn get_bound_params(&self) -> Option<&[GenericParamDef]> {
- match self {
- Self::BoundPredicate { bound_params, .. } | Self::EqPredicate { bound_params, .. } => {
- Some(bound_params)
- }
- _ => None,
- }
- }
}
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
pub(crate) enum GenericParamDefKind {
- Lifetime { outlives: Vec<Lifetime> },
- Type { did: DefId, bounds: Vec<GenericBound>, default: Option<Box<Type>>, synthetic: bool },
+ Lifetime { outlives: ThinVec<Lifetime> },
+ Type { did: DefId, bounds: ThinVec<GenericBound>, default: Option<Box<Type>>, synthetic: bool },
+ // Option<Box<String>> makes this type smaller than `Option<String>` would.
Const { ty: Box<Type>, default: Option<Box<String>>, is_host_effect: bool },
}
@@ -1332,7 +1345,7 @@ pub(crate) struct GenericParamDef {
impl GenericParamDef {
pub(crate) fn lifetime(name: Symbol) -> Self {
- Self { name, kind: GenericParamDefKind::Lifetime { outlives: Vec::new() } }
+ Self { name, kind: GenericParamDefKind::Lifetime { outlives: ThinVec::new() } }
}
pub(crate) fn is_synthetic_param(&self) -> bool {
@@ -1443,6 +1456,9 @@ impl Trait {
pub(crate) fn unsafety(&self, tcx: TyCtxt<'_>) -> hir::Unsafety {
tcx.trait_def(self.def_id).unsafety
}
+ pub(crate) fn is_object_safe(&self, tcx: TyCtxt<'_>) -> bool {
+ tcx.check_is_object_safe(self.def_id)
+ }
}
#[derive(Clone, Debug)]
@@ -2094,9 +2110,8 @@ impl Discriminant {
pub(crate) fn expr(&self, tcx: TyCtxt<'_>) -> Option<String> {
self.expr.map(|body| rendered_const(tcx, body))
}
- /// Will always be a machine readable number, without underscores or suffixes.
- pub(crate) fn value(&self, tcx: TyCtxt<'_>) -> String {
- print_evaluated_const(tcx, self.value, false).unwrap()
+ pub(crate) fn value(&self, tcx: TyCtxt<'_>, with_underscores: bool) -> String {
+ print_evaluated_const(tcx, self.value, with_underscores, false).unwrap()
}
}
@@ -2271,8 +2286,8 @@ pub(crate) struct Static {
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub(crate) struct Constant {
- pub(crate) type_: Type,
- pub(crate) generics: Box<Generics>,
+ pub(crate) type_: Box<Type>,
+ pub(crate) generics: Generics,
pub(crate) kind: ConstantKind,
}
@@ -2341,7 +2356,7 @@ impl ConstantKind {
match *self {
ConstantKind::TyConst { .. } | ConstantKind::Anonymous { .. } => None,
ConstantKind::Extern { def_id } | ConstantKind::Local { def_id, .. } => {
- print_evaluated_const(tcx, def_id, true)
+ print_evaluated_const(tcx, def_id, true, true)
}
}
}
@@ -2510,11 +2525,10 @@ mod size_asserts {
static_assert_size!(DocFragment, 32);
static_assert_size!(GenericArg, 32);
static_assert_size!(GenericArgs, 32);
- static_assert_size!(GenericParamDef, 56);
+ static_assert_size!(GenericParamDef, 40);
static_assert_size!(Generics, 16);
static_assert_size!(Item, 56);
- // FIXME(generic_const_items): Further reduce the size.
- static_assert_size!(ItemKind, 72);
+ static_assert_size!(ItemKind, 56);
static_assert_size!(PathSegment, 40);
static_assert_size!(Type, 32);
// tidy-alphabetical-end
diff --git a/src/librustdoc/clean/types/tests.rs b/src/librustdoc/clean/types/tests.rs
index 4907a5527..ee7c0068e 100644
--- a/src/librustdoc/clean/types/tests.rs
+++ b/src/librustdoc/clean/types/tests.rs
@@ -1,9 +1,8 @@
use super::*;
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;
+use rustc_span::{create_default_session_globals_then, DUMMY_SP};
fn create_doc_fragment(s: &str) -> Vec<DocFragment> {
vec![DocFragment {
diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs
index 61e653423..9ff00c194 100644
--- a/src/librustdoc/clean/utils.rs
+++ b/src/librustdoc/clean/utils.rs
@@ -17,6 +17,7 @@ use rustc_hir::def_id::{DefId, LocalDefId, LOCAL_CRATE};
use rustc_metadata::rendered_const;
use rustc_middle::mir;
use rustc_middle::ty::{self, GenericArgKind, GenericArgsRef, TyCtxt};
+use rustc_middle::ty::{TypeVisitable, TypeVisitableExt};
use rustc_span::symbol::{kw, sym, Symbol};
use std::fmt::Write as _;
use std::mem;
@@ -76,44 +77,119 @@ pub(crate) fn krate(cx: &mut DocContext<'_>) -> Crate {
pub(crate) fn ty_args_to_args<'tcx>(
cx: &mut DocContext<'tcx>,
- args: ty::Binder<'tcx, &'tcx [ty::GenericArg<'tcx>]>,
+ ty_args: ty::Binder<'tcx, &'tcx [ty::GenericArg<'tcx>]>,
has_self: bool,
- container: Option<DefId>,
+ owner: DefId,
) -> Vec<GenericArg> {
- let mut skip_first = has_self;
- let mut ret_val =
- Vec::with_capacity(args.skip_binder().len().saturating_sub(if skip_first { 1 } else { 0 }));
-
- ret_val.extend(args.iter().enumerate().filter_map(|(index, kind)| {
- match kind.skip_binder().unpack() {
- GenericArgKind::Lifetime(lt) => {
- Some(GenericArg::Lifetime(clean_middle_region(lt).unwrap_or(Lifetime::elided())))
- }
- GenericArgKind::Type(_) if skip_first => {
- skip_first = false;
- None
+ if ty_args.skip_binder().is_empty() {
+ // Fast path which avoids executing the query `generics_of`.
+ return Vec::new();
+ }
+
+ let params = &cx.tcx.generics_of(owner).params;
+ let mut elision_has_failed_once_before = false;
+
+ let offset = if has_self { 1 } else { 0 };
+ let mut args = Vec::with_capacity(ty_args.skip_binder().len().saturating_sub(offset));
+
+ let ty_arg_to_arg = |(index, arg): (usize, &ty::GenericArg<'tcx>)| match arg.unpack() {
+ GenericArgKind::Lifetime(lt) => {
+ Some(GenericArg::Lifetime(clean_middle_region(lt).unwrap_or(Lifetime::elided())))
+ }
+ GenericArgKind::Type(_) if has_self && index == 0 => None,
+ GenericArgKind::Type(ty) => {
+ if !elision_has_failed_once_before
+ && let Some(default) = params[index].default_value(cx.tcx)
+ {
+ let default =
+ ty_args.map_bound(|args| default.instantiate(cx.tcx, args).expect_ty());
+
+ if can_elide_generic_arg(ty_args.rebind(ty), default) {
+ return None;
+ }
+
+ elision_has_failed_once_before = true;
}
- GenericArgKind::Type(ty) => Some(GenericArg::Type(clean_middle_ty(
- kind.rebind(ty),
+
+ Some(GenericArg::Type(clean_middle_ty(
+ ty_args.rebind(ty),
cx,
None,
- container.map(|container| crate::clean::ContainerTy::Regular {
- ty: container,
- args,
+ Some(crate::clean::ContainerTy::Regular {
+ ty: owner,
+ args: ty_args,
has_self,
arg: index,
}),
- ))),
- // FIXME(effects): this relies on the host effect being called `host`, which users could also name
- // their const generics.
- // FIXME(effects): this causes `host = true` and `host = false` generics to also be emitted.
- GenericArgKind::Const(ct) if let ty::ConstKind::Param(p) = ct.kind() && p.name == sym::host => None,
- GenericArgKind::Const(ct) => {
- Some(GenericArg::Const(Box::new(clean_middle_const(kind.rebind(ct), cx))))
+ )))
+ }
+ GenericArgKind::Const(ct) => {
+ if let ty::GenericParamDefKind::Const { is_host_effect: true, .. } = params[index].kind
+ {
+ return None;
+ }
+
+ if !elision_has_failed_once_before
+ && let Some(default) = params[index].default_value(cx.tcx)
+ {
+ let default =
+ ty_args.map_bound(|args| default.instantiate(cx.tcx, args).expect_const());
+
+ if can_elide_generic_arg(ty_args.rebind(ct), default) {
+ return None;
+ }
+
+ elision_has_failed_once_before = true;
}
+
+ Some(GenericArg::Const(Box::new(clean_middle_const(ty_args.rebind(ct), cx))))
}
- }));
- ret_val
+ };
+
+ args.extend(ty_args.skip_binder().iter().enumerate().rev().filter_map(ty_arg_to_arg));
+ args.reverse();
+ args
+}
+
+/// Check if the generic argument `actual` coincides with the `default` and can therefore be elided.
+///
+/// This uses a very conservative approach for performance and correctness reasons, meaning for
+/// several classes of terms it claims that they cannot be elided even if they theoretically could.
+/// This is absolutely fine since it mostly concerns edge cases.
+fn can_elide_generic_arg<'tcx, Term>(
+ actual: ty::Binder<'tcx, Term>,
+ default: ty::Binder<'tcx, Term>,
+) -> bool
+where
+ Term: Eq + TypeVisitable<TyCtxt<'tcx>>,
+{
+ // In practice, we shouldn't have any inference variables at this point.
+ // However to be safe, we bail out if we do happen to stumble upon them.
+ if actual.has_infer() || default.has_infer() {
+ return false;
+ }
+
+ // Since we don't properly keep track of bound variables in rustdoc (yet), we don't attempt to
+ // make any sense out of escaping bound variables. We simply don't have enough context and it
+ // would be incorrect to try to do so anyway.
+ if actual.has_escaping_bound_vars() || default.has_escaping_bound_vars() {
+ return false;
+ }
+
+ // Theoretically we could now check if either term contains (non-escaping) late-bound regions or
+ // projections, relate the two using an `InferCtxt` and check if the resulting obligations hold.
+ // Having projections means that the terms can potentially be further normalized thereby possibly
+ // revealing that they are equal after all. Regarding late-bound regions, they could to be
+ // liberated allowing us to consider more types to be equal by ignoring the names of binders
+ // (e.g., `for<'a> TYPE<'a>` and `for<'b> TYPE<'b>`).
+ //
+ // However, we are mostly interested in “reeliding” generic args, i.e., eliding generic args that
+ // were originally elided by the user and later filled in by the compiler contrary to eliding
+ // arbitrary generic arguments if they happen to semantically coincide with the default (of course,
+ // we cannot possibly distinguish these two cases). Therefore and for performance reasons, it
+ // suffices to only perform a syntactic / structural check by comparing the memory addresses of
+ // the interned arguments.
+ actual.skip_binder() == default.skip_binder()
}
fn external_generic_args<'tcx>(
@@ -123,7 +199,7 @@ fn external_generic_args<'tcx>(
bindings: ThinVec<TypeBinding>,
ty_args: ty::Binder<'tcx, GenericArgsRef<'tcx>>,
) -> GenericArgs {
- let args = ty_args_to_args(cx, ty_args.map_bound(|args| &args[..]), has_self, Some(did));
+ let args = ty_args_to_args(cx, ty_args.map_bound(|args| &args[..]), has_self, did);
if cx.tcx.fn_trait_kind_from_def_id(did).is_some() {
let ty = ty_args
@@ -279,7 +355,8 @@ pub(crate) fn print_const(cx: &DocContext<'_>, n: ty::Const<'_>) -> String {
pub(crate) fn print_evaluated_const(
tcx: TyCtxt<'_>,
def_id: DefId,
- underscores_and_type: bool,
+ with_underscores: bool,
+ with_type: bool,
) -> Option<String> {
tcx.const_eval_poly(def_id).ok().and_then(|val| {
let ty = tcx.type_of(def_id).instantiate_identity();
@@ -288,7 +365,7 @@ pub(crate) fn print_evaluated_const(
(mir::ConstValue::Scalar(_), &ty::Adt(_, _)) => None,
(mir::ConstValue::Scalar(_), _) => {
let const_ = mir::Const::from_value(val, ty);
- Some(print_const_with_custom_print_scalar(tcx, const_, underscores_and_type))
+ Some(print_const_with_custom_print_scalar(tcx, const_, with_underscores, with_type))
}
_ => None,
}
@@ -324,32 +401,37 @@ fn format_integer_with_underscore_sep(num: &str) -> String {
fn print_const_with_custom_print_scalar<'tcx>(
tcx: TyCtxt<'tcx>,
ct: mir::Const<'tcx>,
- underscores_and_type: bool,
+ with_underscores: bool,
+ with_type: bool,
) -> String {
// Use a slightly different format for integer types which always shows the actual value.
// For all other types, fallback to the original `pretty_print_const`.
match (ct, ct.ty().kind()) {
(mir::Const::Val(mir::ConstValue::Scalar(int), _), ty::Uint(ui)) => {
- if underscores_and_type {
- format!("{}{}", format_integer_with_underscore_sep(&int.to_string()), ui.name_str())
+ let mut output = if with_underscores {
+ format_integer_with_underscore_sep(&int.to_string())
} else {
int.to_string()
+ };
+ if with_type {
+ output += ui.name_str();
}
+ output
}
(mir::Const::Val(mir::ConstValue::Scalar(int), _), ty::Int(i)) => {
let ty = ct.ty();
let size = tcx.layout_of(ty::ParamEnv::empty().and(ty)).unwrap().size;
let data = int.assert_bits(size);
let sign_extended_data = size.sign_extend(data) as i128;
- if underscores_and_type {
- format!(
- "{}{}",
- format_integer_with_underscore_sep(&sign_extended_data.to_string()),
- i.name_str()
- )
+ let mut output = if with_underscores {
+ format_integer_with_underscore_sep(&sign_extended_data.to_string())
} else {
sign_extended_data.to_string()
+ };
+ if with_type {
+ output += i.name_str();
}
+ output
}
_ => ct.to_string(),
}
@@ -502,7 +584,7 @@ pub(crate) fn has_doc_flag(tcx: TyCtxt<'_>, did: DefId, flag: Symbol) -> bool {
/// Set by `bootstrap::Builder::doc_rust_lang_org_channel` in order to keep tests passing on beta/stable.
pub(crate) const DOC_RUST_LANG_ORG_CHANNEL: &str = env!("DOC_RUST_LANG_ORG_CHANNEL");
pub(crate) static DOC_CHANNEL: Lazy<&'static str> =
- Lazy::new(|| DOC_RUST_LANG_ORG_CHANNEL.rsplit("/").filter(|c| !c.is_empty()).next().unwrap());
+ Lazy::new(|| DOC_RUST_LANG_ORG_CHANNEL.rsplit('/').filter(|c| !c.is_empty()).next().unwrap());
/// Render a sequence of macro arms in a format suitable for displaying to the user
/// as part of an item declaration.
diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs
index 3e6066c78..6d9f8b820 100644
--- a/src/librustdoc/core.rs
+++ b/src/librustdoc/core.rs
@@ -14,8 +14,8 @@ use rustc_lint::{late_lint_mod, MissingDoc};
use rustc_middle::hir::nested_filter;
use rustc_middle::ty::{ParamEnv, Ty, TyCtxt};
use rustc_session::config::{self, CrateType, ErrorOutputType, ResolveDocLinks};
+use rustc_session::lint;
use rustc_session::Session;
-use rustc_session::{lint, EarlyErrorHandler};
use rustc_span::symbol::sym;
use rustc_span::{source_map, Span};
@@ -23,6 +23,7 @@ use std::cell::RefCell;
use std::mem;
use std::rc::Rc;
use std::sync::LazyLock;
+use std::sync::{atomic::AtomicBool, Arc};
use crate::clean::inline::build_external_trait;
use crate::clean::{self, ItemId};
@@ -174,7 +175,6 @@ pub(crate) fn new_handler(
/// Parse, resolve, and typecheck the given crate.
pub(crate) fn create_config(
- handler: &EarlyErrorHandler,
RustdocOptions {
input,
crate_name,
@@ -198,6 +198,7 @@ pub(crate) fn create_config(
..
}: RustdocOptions,
RenderOptions { document_private, .. }: &RenderOptions,
+ using_internal_features: Arc<AtomicBool>,
) -> rustc_interface::Config {
// Add the doc cfg into the doc build.
cfgs.push("doc".to_string());
@@ -253,8 +254,8 @@ pub(crate) fn create_config(
interface::Config {
opts: sessopts,
- crate_cfg: interface::parse_cfgspecs(handler, cfgs),
- crate_check_cfg: interface::parse_check_cfg(handler, check_cfgs),
+ crate_cfg: cfgs,
+ crate_check_cfg: check_cfgs,
input,
output_file: None,
output_dir: None,
@@ -262,6 +263,7 @@ pub(crate) fn create_config(
locale_resources: rustc_driver::DEFAULT_LOCALE_RESOURCES,
lint_caps,
parse_sess_created: None,
+ hash_untracked_state: None,
register_lints: Some(Box::new(crate::lint::register_lints)),
override_queries: Some(|_sess, providers| {
// We do not register late module lints, so this only runs `MissingDoc`.
@@ -292,6 +294,7 @@ pub(crate) fn create_config(
make_codegen_backend: None,
registry: rustc_driver::diagnostics_registry(),
ice_file: None,
+ using_internal_features,
expanded_args,
}
}
@@ -316,10 +319,14 @@ pub(crate) fn run_global_ctxt(
tcx.hir().for_each_module(|module| tcx.ensure().collect_mod_item_types(module))
});
- // NOTE: This is copy/pasted from typeck/lib.rs and should be kept in sync with those changes.
+ // NOTE: These are copy/pasted from typeck/lib.rs and should be kept in sync with those changes.
+ let _ = tcx.sess.time("wf_checking", || {
+ tcx.hir().try_par_for_each_module(|module| tcx.ensure().check_mod_type_wf(module))
+ });
tcx.sess.time("item_types_checking", || {
tcx.hir().for_each_module(|module| tcx.ensure().check_mod_item_types(module))
});
+
tcx.sess.abort_if_errors();
tcx.sess.time("missing_docs", || rustc_lint::check_crate(tcx));
tcx.sess.time("check_mod_attrs", || {
diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs
index 741d329fb..241286580 100644
--- a/src/librustdoc/doctest.rs
+++ b/src/librustdoc/doctest.rs
@@ -13,7 +13,7 @@ use rustc_parse::parser::attr::InnerAttrPolicy;
use rustc_resolve::rustdoc::span_of_fragments;
use rustc_session::config::{self, CrateType, ErrorOutputType};
use rustc_session::parse::ParseSess;
-use rustc_session::{lint, EarlyErrorHandler, Session};
+use rustc_session::{lint, Session};
use rustc_span::edition::Edition;
use rustc_span::source_map::SourceMap;
use rustc_span::symbol::sym;
@@ -85,18 +85,13 @@ pub(crate) fn run(options: RustdocOptions) -> Result<(), ErrorGuaranteed> {
..config::Options::default()
};
- let early_error_handler = EarlyErrorHandler::new(ErrorOutputType::default());
-
let mut cfgs = options.cfgs.clone();
cfgs.push("doc".to_owned());
cfgs.push("doctest".to_owned());
let config = interface::Config {
opts: sessopts,
- crate_cfg: interface::parse_cfgspecs(&early_error_handler, cfgs),
- crate_check_cfg: interface::parse_check_cfg(
- &early_error_handler,
- options.check_cfgs.clone(),
- ),
+ crate_cfg: cfgs,
+ crate_check_cfg: options.check_cfgs.clone(),
input,
output_file: None,
output_dir: None,
@@ -104,11 +99,13 @@ pub(crate) fn run(options: RustdocOptions) -> Result<(), ErrorGuaranteed> {
locale_resources: rustc_driver::DEFAULT_LOCALE_RESOURCES,
lint_caps,
parse_sess_created: None,
+ hash_untracked_state: None,
register_lints: Some(Box::new(crate::lint::register_lints)),
override_queries: None,
make_codegen_backend: None,
registry: rustc_driver::diagnostics_registry(),
ice_file: None,
+ using_internal_features: Arc::default(),
expanded_args: options.expanded_args.clone(),
};
diff --git a/src/librustdoc/formats/cache.rs b/src/librustdoc/formats/cache.rs
index 4c6e7dfb9..abff77253 100644
--- a/src/librustdoc/formats/cache.rs
+++ b/src/librustdoc/formats/cache.rs
@@ -50,8 +50,8 @@ pub(crate) struct Cache {
/// Unlike 'paths', this mapping ignores any renames that occur
/// due to 'use' statements.
///
- /// This map is used when writing out the special 'implementors'
- /// javascript file. By using the exact path that the type
+ /// This map is used when writing out the `impl.trait` and `impl.type`
+ /// javascript files. By using the exact path that the type
/// is declared with, we ensure that each path will be identical
/// 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
@@ -221,19 +221,25 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> {
_ => self.cache.stripped_mod,
};
+ #[inline]
+ fn is_from_private_dep(tcx: TyCtxt<'_>, cache: &Cache, def_id: DefId) -> bool {
+ let krate = def_id.krate;
+
+ cache.masked_crates.contains(&krate) || tcx.is_private_dep(krate)
+ }
+
// If the impl is from a masked crate or references something from a
// masked crate then remove it completely.
- if let clean::ImplItem(ref i) = *item.kind {
- if self.cache.masked_crates.contains(&item.item_id.krate())
+ if let clean::ImplItem(ref i) = *item.kind &&
+ (self.cache.masked_crates.contains(&item.item_id.krate())
|| i.trait_
.as_ref()
- .map_or(false, |t| self.cache.masked_crates.contains(&t.def_id().krate))
+ .map_or(false, |t| is_from_private_dep(self.tcx, self.cache, t.def_id()))
|| i.for_
.def_id(self.cache)
- .map_or(false, |d| self.cache.masked_crates.contains(&d.krate))
- {
- return None;
- }
+ .map_or(false, |d| is_from_private_dep(self.tcx, self.cache, d)))
+ {
+ return None;
}
// Propagate a trait method's documentation to all implementors of the
@@ -334,33 +340,37 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> {
// A crate has a module at its root, containing all items,
// which should not be indexed. The crate-item itself is
// inserted later on when serializing the search-index.
- if item.item_id.as_def_id().map_or(false, |idx| !idx.is_crate_root()) {
+ if item.item_id.as_def_id().map_or(false, |idx| !idx.is_crate_root())
+ && let ty = item.type_()
+ && (ty != ItemType::StructField
+ || u16::from_str_radix(s.as_str(), 10).is_err())
+ {
let desc =
short_markdown_summary(&item.doc_value(), &item.link_names(self.cache));
- let ty = item.type_();
- if ty != ItemType::StructField
- || u16::from_str_radix(s.as_str(), 10).is_err()
- {
- // In case this is a field from a tuple struct, we don't add it into
- // the search index because its name is something like "0", which is
- // not useful for rustdoc search.
- self.cache.search_index.push(IndexItem {
- ty,
- name: s,
- path: join_with_double_colon(path),
- desc,
- parent,
- parent_idx: None,
- search_type: get_function_type_for_search(
- &item,
- self.tcx,
- clean_impl_generics(self.cache.parent_stack.last()).as_ref(),
- self.cache,
- ),
- aliases: item.attrs.get_doc_aliases(),
- deprecation: item.deprecation(self.tcx),
- });
- }
+ // In case this is a field from a tuple struct, we don't add it into
+ // the search index because its name is something like "0", which is
+ // not useful for rustdoc search.
+ self.cache.search_index.push(IndexItem {
+ ty,
+ name: s,
+ path: join_with_double_colon(path),
+ desc,
+ parent,
+ parent_idx: None,
+ impl_id: if let Some(ParentStackItem::Impl { item_id, .. }) = self.cache.parent_stack.last() {
+ item_id.as_def_id()
+ } else {
+ None
+ },
+ search_type: get_function_type_for_search(
+ &item,
+ self.tcx,
+ clean_impl_generics(self.cache.parent_stack.last()).as_ref(),
+ self.cache,
+ ),
+ aliases: item.attrs.get_doc_aliases(),
+ deprecation: item.deprecation(self.tcx),
+ });
}
}
(Some(parent), None) if is_inherent_impl_item => {
@@ -371,6 +381,13 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> {
parent,
item: item.clone(),
impl_generics,
+ impl_id: if let Some(ParentStackItem::Impl { item_id, .. }) =
+ self.cache.parent_stack.last()
+ {
+ item_id.as_def_id()
+ } else {
+ None
+ },
});
}
_ => {}
@@ -541,6 +558,7 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> {
pub(crate) struct OrphanImplItem {
pub(crate) parent: DefId,
+ pub(crate) impl_id: Option<DefId>,
pub(crate) item: clean::Item,
pub(crate) impl_generics: Option<(clean::Type, clean::Generics)>,
}
diff --git a/src/librustdoc/formats/item_type.rs b/src/librustdoc/formats/item_type.rs
index be2ee7915..def3a90c8 100644
--- a/src/librustdoc/formats/item_type.rs
+++ b/src/librustdoc/formats/item_type.rs
@@ -141,7 +141,7 @@ impl From<DefKind> for ItemType {
| DefKind::GlobalAsm
| DefKind::Impl { .. }
| DefKind::Closure
- | DefKind::Generator => Self::ForeignType,
+ | DefKind::Coroutine => Self::ForeignType,
}
}
}
@@ -180,6 +180,9 @@ impl ItemType {
pub(crate) fn is_method(&self) -> bool {
matches!(*self, ItemType::Method | ItemType::TyMethod)
}
+ pub(crate) fn is_adt(&self) -> bool {
+ matches!(*self, ItemType::Struct | ItemType::Union | ItemType::Enum)
+ }
}
impl fmt::Display for ItemType {
diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs
index 2751b6613..29fd880af 100644
--- a/src/librustdoc/html/format.rs
+++ b/src/librustdoc/html/format.rs
@@ -325,8 +325,7 @@ pub(crate) fn print_where_clause<'a, 'tcx: 'a>(
bounds_display.truncate(bounds_display.len() - " + ".len());
write!(f, "{}: {bounds_display}", lifetime.print())
}
- // FIXME(fmease): Render bound params.
- clean::WherePredicate::EqPredicate { lhs, rhs, bound_params: _ } => {
+ clean::WherePredicate::EqPredicate { lhs, rhs } => {
if f.alternate() {
write!(f, "{:#} == {:#}", lhs.print(cx), rhs.print(cx))
} else {
@@ -848,7 +847,7 @@ fn resolved_path<'cx>(
fn primitive_link(
f: &mut fmt::Formatter<'_>,
prim: clean::PrimitiveType,
- name: &str,
+ name: fmt::Arguments<'_>,
cx: &Context<'_>,
) -> fmt::Result {
primitive_link_fragment(f, prim, name, "", cx)
@@ -857,7 +856,7 @@ fn primitive_link(
fn primitive_link_fragment(
f: &mut fmt::Formatter<'_>,
prim: clean::PrimitiveType,
- name: &str,
+ name: fmt::Arguments<'_>,
fragment: &str,
cx: &Context<'_>,
) -> fmt::Result {
@@ -908,7 +907,7 @@ fn primitive_link_fragment(
None => {}
}
}
- f.write_str(name)?;
+ std::fmt::Display::fmt(&name, f)?;
if needs_termination {
write!(f, "</a>")?;
}
@@ -978,9 +977,11 @@ fn fmt_type<'cx>(
}
clean::Infer => write!(f, "_"),
clean::Primitive(clean::PrimitiveType::Never) => {
- primitive_link(f, PrimitiveType::Never, "!", cx)
+ primitive_link(f, PrimitiveType::Never, format_args!("!"), cx)
+ }
+ clean::Primitive(prim) => {
+ primitive_link(f, prim, format_args!("{}", prim.as_sym().as_str()), cx)
}
- clean::Primitive(prim) => primitive_link(f, prim, prim.as_sym().as_str(), cx),
clean::BareFunction(ref decl) => {
if f.alternate() {
write!(
@@ -999,16 +1000,16 @@ fn fmt_type<'cx>(
decl.unsafety.print_with_space(),
print_abi_with_space(decl.abi)
)?;
- primitive_link(f, PrimitiveType::Fn, "fn", cx)?;
+ primitive_link(f, PrimitiveType::Fn, format_args!("fn"), cx)?;
write!(f, "{}", decl.decl.print(cx))
}
}
clean::Tuple(ref typs) => {
match &typs[..] {
- &[] => primitive_link(f, PrimitiveType::Unit, "()", cx),
+ &[] => primitive_link(f, PrimitiveType::Unit, format_args!("()"), cx),
[one] => {
if let clean::Generic(name) = one {
- primitive_link(f, PrimitiveType::Tuple, &format!("({name},)"), cx)
+ primitive_link(f, PrimitiveType::Tuple, format_args!("({name},)"), cx)
} else {
write!(f, "(")?;
// Carry `f.alternate()` into this display w/o branching manually.
@@ -1029,7 +1030,10 @@ fn fmt_type<'cx>(
primitive_link(
f,
PrimitiveType::Tuple,
- &format!("({})", generic_names.iter().map(|s| s.as_str()).join(", ")),
+ format_args!(
+ "({})",
+ generic_names.iter().map(|s| s.as_str()).join(", ")
+ ),
cx,
)
} else {
@@ -1048,7 +1052,7 @@ fn fmt_type<'cx>(
}
clean::Slice(ref t) => match **t {
clean::Generic(name) => {
- primitive_link(f, PrimitiveType::Slice, &format!("[{name}]"), cx)
+ primitive_link(f, PrimitiveType::Slice, format_args!("[{name}]"), cx)
}
_ => {
write!(f, "[")?;
@@ -1060,7 +1064,7 @@ fn fmt_type<'cx>(
clean::Generic(name) if !f.alternate() => primitive_link(
f,
PrimitiveType::Array,
- &format!("[{name}; {n}]", n = Escape(n)),
+ format_args!("[{name}; {n}]", n = Escape(n)),
cx,
),
_ => {
@@ -1070,7 +1074,12 @@ fn fmt_type<'cx>(
write!(f, "; {n}")?;
} else {
write!(f, "; ")?;
- primitive_link(f, PrimitiveType::Array, &format!("{n}", n = Escape(n)), cx)?;
+ primitive_link(
+ f,
+ PrimitiveType::Array,
+ format_args!("{n}", n = Escape(n)),
+ cx,
+ )?;
}
write!(f, "]")
}
@@ -1082,22 +1091,32 @@ fn fmt_type<'cx>(
};
if matches!(**t, clean::Generic(_)) || t.is_assoc_ty() {
- let text = if f.alternate() {
- format!("*{m} {ty:#}", ty = t.print(cx))
+ let ty = t.print(cx);
+ if f.alternate() {
+ primitive_link(
+ f,
+ clean::PrimitiveType::RawPointer,
+ format_args!("*{m} {ty:#}"),
+ cx,
+ )
} else {
- format!("*{m} {ty}", ty = t.print(cx))
- };
- primitive_link(f, clean::PrimitiveType::RawPointer, &text, cx)
+ primitive_link(
+ f,
+ clean::PrimitiveType::RawPointer,
+ format_args!("*{m} {ty}"),
+ cx,
+ )
+ }
} else {
- primitive_link(f, clean::PrimitiveType::RawPointer, &format!("*{m} "), cx)?;
+ primitive_link(f, clean::PrimitiveType::RawPointer, format_args!("*{m} "), cx)?;
fmt::Display::fmt(&t.print(cx), f)
}
}
clean::BorrowedRef { lifetime: ref l, mutability, type_: ref ty } => {
- let lt = match l {
- Some(l) => format!("{} ", l.print()),
- _ => String::new(),
- };
+ let lt = display_fn(|f| match l {
+ Some(l) => write!(f, "{} ", l.print()),
+ _ => Ok(()),
+ });
let m = mutability.print_with_space();
let amp = if f.alternate() { "&" } else { "&amp;" };
@@ -1105,7 +1124,7 @@ fn fmt_type<'cx>(
return primitive_link(
f,
PrimitiveType::Reference,
- &format!("{amp}{lt}{m}{name}"),
+ format_args!("{amp}{lt}{m}{name}"),
cx,
);
}
@@ -1255,7 +1274,7 @@ impl clean::Impl {
{
// Hardcoded anchor library/core/src/primitive_docs.rs
// Link should match `# Trait implementations`
- primitive_link_fragment(f, PrimitiveType::Tuple, &format!("({name}₁, {name}₂, …, {name}ₙ)"), "#trait-implementations-1", cx)?;
+ primitive_link_fragment(f, PrimitiveType::Tuple, format_args!("({name}₁, {name}₂, …, {name}ₙ)"), "#trait-implementations-1", cx)?;
} else if let clean::BareFunction(bare_fn) = &self.for_ &&
let [clean::Argument { type_: clean::Type::Generic(name), .. }] = &bare_fn.decl.inputs.values[..] &&
(self.kind.is_fake_variadic() || self.kind.is_auto())
@@ -1282,7 +1301,7 @@ impl clean::Impl {
} else {
""
};
- primitive_link_fragment(f, PrimitiveType::Tuple, &format!("fn ({name}₁, {name}₂, …, {name}ₙ{ellipsis})"), "#trait-implementations-1", cx)?;
+ primitive_link_fragment(f, PrimitiveType::Tuple, format_args!("fn ({name}₁, {name}₂, …, {name}ₙ{ellipsis})"), "#trait-implementations-1", cx)?;
// Write output.
if !bare_fn.decl.output.is_unit() {
write!(f, " -> ")?;
@@ -1666,7 +1685,12 @@ impl clean::ImportSource {
}
let name = self.path.last();
if let hir::def::Res::PrimTy(p) = self.path.res {
- primitive_link(f, PrimitiveType::from(p), name.as_str(), cx)?;
+ primitive_link(
+ f,
+ PrimitiveType::from(p),
+ format_args!("{}", name.as_str()),
+ cx,
+ )?;
} else {
f.write_str(name.as_str())?;
}
diff --git a/src/librustdoc/html/layout.rs b/src/librustdoc/html/layout.rs
index 8c5871d91..d4b4db0f3 100644
--- a/src/librustdoc/html/layout.rs
+++ b/src/librustdoc/html/layout.rs
@@ -17,6 +17,7 @@ pub(crate) struct Layout {
pub(crate) external_html: ExternalHtml,
pub(crate) default_settings: FxHashMap<String, String>,
pub(crate) krate: String,
+ pub(crate) krate_version: String,
/// The given user css file which allow to customize the generated
/// documentation theme.
pub(crate) css_file_extension: Option<PathBuf>,
@@ -31,6 +32,7 @@ pub(crate) struct Page<'a> {
pub(crate) static_root_path: Option<&'a str>,
pub(crate) description: &'a str,
pub(crate) resource_suffix: &'a str,
+ pub(crate) rust_logo: bool,
}
impl<'a> Page<'a> {
@@ -54,9 +56,19 @@ struct PageLayout<'a> {
themes: Vec<String>,
sidebar: String,
content: String,
- krate_with_trailing_slash: String,
rust_channel: &'static str,
pub(crate) rustdoc_version: &'a str,
+ // same as layout.krate, except on top-level pages like
+ // Settings, Help, All Crates, and About Scraped Examples,
+ // where these things instead give Rustdoc name and version.
+ //
+ // These are separate from the variables used for the search
+ // engine, because "Rustdoc" isn't necessarily a crate in
+ // the current workspace.
+ display_krate: &'a str,
+ display_krate_with_trailing_slash: String,
+ display_krate_version_number: &'a str,
+ display_krate_version_extra: &'a str,
}
pub(crate) fn render<T: Print, S: Print>(
@@ -66,12 +78,26 @@ pub(crate) fn render<T: Print, S: Print>(
t: T,
style_files: &[StylePath],
) -> String {
+ let rustdoc_version = rustc_interface::util::version_str!().unwrap_or("unknown version");
+
+ let (display_krate, display_krate_version, display_krate_with_trailing_slash) =
+ if page.root_path == "./" {
+ // top level pages use Rust branding
+ ("Rustdoc", rustdoc_version, String::new())
+ } else {
+ let display_krate_with_trailing_slash =
+ ensure_trailing_slash(&layout.krate).to_string();
+ (&layout.krate[..], &layout.krate_version[..], display_krate_with_trailing_slash)
+ };
let static_root_path = page.get_static_root_path();
- let krate_with_trailing_slash = ensure_trailing_slash(&layout.krate).to_string();
+
+ // bootstrap passes in parts of the version separated by tabs, but other stuff might use spaces
+ let (display_krate_version_number, display_krate_version_extra) =
+ display_krate_version.split_once([' ', '\t']).unwrap_or((display_krate_version, ""));
+
let mut themes: Vec<String> = style_files.iter().map(|s| s.basename().unwrap()).collect();
themes.sort();
- let rustdoc_version = rustc_interface::util::version_str!().unwrap_or("unknown version");
let content = Buffer::html().to_display(t); // Note: This must happen before making the sidebar.
let sidebar = Buffer::html().to_display(sidebar);
PageLayout {
@@ -82,7 +108,10 @@ pub(crate) fn render<T: Print, S: Print>(
themes,
sidebar,
content,
- krate_with_trailing_slash,
+ display_krate,
+ display_krate_with_trailing_slash,
+ display_krate_version_number,
+ display_krate_version_extra,
rust_channel: *crate::clean::utils::DOC_CHANNEL,
rustdoc_version,
}
diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs
index d24e6e5fa..2807dfed0 100644
--- a/src/librustdoc/html/markdown.rs
+++ b/src/librustdoc/html/markdown.rs
@@ -1750,7 +1750,7 @@ pub(crate) fn markdown_links<'md, R>(
}
// do not actually include braces in the span
let range = (open_brace + 1)..close_brace;
- MarkdownLinkRange::Destination(range.clone())
+ MarkdownLinkRange::Destination(range)
};
let span_for_offset_forward = |span: Range<usize>, open: u8, close: u8| {
@@ -1786,7 +1786,7 @@ pub(crate) fn markdown_links<'md, R>(
}
// do not actually include braces in the span
let range = (open_brace + 1)..close_brace;
- MarkdownLinkRange::Destination(range.clone())
+ MarkdownLinkRange::Destination(range)
};
let mut broken_link_callback = |link: BrokenLink<'md>| Some((link.reference, "".into()));
@@ -2024,6 +2024,7 @@ fn init_id_map() -> FxHashMap<Cow<'static, str>, usize> {
map.insert("required-associated-consts".into(), 1);
map.insert("required-methods".into(), 1);
map.insert("provided-methods".into(), 1);
+ map.insert("object-safety".into(), 1);
map.insert("implementors".into(), 1);
map.insert("synthetic-implementors".into(), 1);
map.insert("implementations-list".into(), 1);
diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs
index 97714afaa..50777134d 100644
--- a/src/librustdoc/html/render/context.rs
+++ b/src/librustdoc/html/render/context.rs
@@ -7,13 +7,10 @@ use std::sync::mpsc::{channel, Receiver};
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_hir::def_id::{DefIdMap, LOCAL_CRATE};
-use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams};
use rustc_middle::ty::TyCtxt;
use rustc_session::Session;
-use rustc_span::def_id::DefId;
use rustc_span::edition::Edition;
-use rustc_span::source_map::FileName;
-use rustc_span::{sym, Symbol};
+use rustc_span::{sym, FileName, Symbol};
use super::print_item::{full_path, item_path, print_item};
use super::search_index::build_index;
@@ -24,13 +21,14 @@ use super::{
sidebar::{sidebar_module_like, Sidebar},
AllTypes, LinkFromSrc, StylePath,
};
-use crate::clean::{self, types::ExternalLocation, ExternalCrate, TypeAliasItem};
+use crate::clean::utils::has_doc_flag;
+use crate::clean::{self, types::ExternalLocation, ExternalCrate};
use crate::config::{ModuleSorting, RenderOptions};
use crate::docfs::{DocFS, PathError};
use crate::error::Error;
use crate::formats::cache::Cache;
use crate::formats::item_type::ItemType;
-use crate::formats::{self, FormatRenderer};
+use crate::formats::FormatRenderer;
use crate::html::escape::Escape;
use crate::html::format::{join_with_double_colon, Buffer};
use crate::html::markdown::{self, plain_text_summary, ErrorCodes, IdMap};
@@ -149,53 +147,6 @@ impl SharedContext<'_> {
pub(crate) fn edition(&self) -> Edition {
self.tcx.sess.edition()
}
-
- /// Returns a list of impls on the given type, and, if it's a type alias,
- /// other types that it aliases.
- pub(crate) fn all_impls_for_item<'a>(
- &'a self,
- it: &clean::Item,
- did: DefId,
- ) -> Vec<&'a formats::Impl> {
- let tcx = self.tcx;
- let cache = &self.cache;
- let mut saw_impls = FxHashSet::default();
- let mut v: Vec<&formats::Impl> = cache
- .impls
- .get(&did)
- .map(Vec::as_slice)
- .unwrap_or(&[])
- .iter()
- .filter(|i| saw_impls.insert(i.def_id()))
- .collect();
- if let TypeAliasItem(ait) = &*it.kind &&
- let aliased_clean_type = ait.item_type.as_ref().unwrap_or(&ait.type_) &&
- let Some(aliased_type_defid) = aliased_clean_type.def_id(cache) &&
- let Some(av) = cache.impls.get(&aliased_type_defid) &&
- let Some(alias_def_id) = it.item_id.as_def_id()
- {
- // This branch of the compiler compares types structually, but does
- // not check trait bounds. That's probably fine, since type aliases
- // don't normally constrain on them anyway.
- // https://github.com/rust-lang/rust/issues/21903
- //
- // FIXME(lazy_type_alias): Once the feature is complete or stable, rewrite this to use type unification.
- // Be aware of `tests/rustdoc/issue-112515-impl-ty-alias.rs` which might regress.
- let aliased_ty = tcx.type_of(alias_def_id).skip_binder();
- let reject_cx = DeepRejectCtxt {
- treat_obligation_params: TreatParams::AsCandidateKey,
- };
- v.extend(av.iter().filter(|impl_| {
- if let Some(impl_def_id) = impl_.impl_item.item_id.as_def_id() {
- reject_cx.types_may_unify(aliased_ty, tcx.type_of(impl_def_id).skip_binder())
- && saw_impls.insert(impl_def_id)
- } else {
- false
- }
- }));
- }
- v
- }
}
impl<'tcx> Context<'tcx> {
@@ -277,6 +228,7 @@ impl<'tcx> Context<'tcx> {
title: &title,
description: &desc,
resource_suffix: &clone_shared.resource_suffix,
+ rust_logo: has_doc_flag(self.tcx(), LOCAL_CRATE.as_def_id(), sym::rust_logo),
};
let mut page_buffer = Buffer::html();
print_item(self, it, &mut page_buffer, &page);
@@ -528,12 +480,14 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
if let Some(url) = playground_url {
playground = Some(markdown::Playground { crate_name: Some(krate.name(tcx)), url });
}
+ let krate_version = cache.crate_version.as_deref().unwrap_or_default();
let mut layout = layout::Layout {
logo: String::new(),
favicon: String::new(),
external_html,
default_settings,
krate: krate.name(tcx).to_string(),
+ krate_version: krate_version.to_string(),
css_file_extension: extension_css,
scrape_examples_extension: !call_locations.is_empty(),
};
@@ -658,21 +612,22 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
let shared = Rc::clone(&self.shared);
let mut page = layout::Page {
title: "List of all items in this crate",
- css_class: "mod",
+ css_class: "mod sys",
root_path: "../",
static_root_path: shared.static_root_path.as_deref(),
description: "List of all items in this crate",
resource_suffix: &shared.resource_suffix,
+ rust_logo: has_doc_flag(self.tcx(), LOCAL_CRATE.as_def_id(), sym::rust_logo),
};
let all = shared.all.replace(AllTypes::new());
let mut sidebar = Buffer::html();
let blocks = sidebar_module_like(all.item_sections());
let bar = Sidebar {
- title_prefix: "Crate ",
- title: crate_name.as_str(),
+ title_prefix: "",
+ title: "",
is_crate: false,
- version: "",
+ is_mod: false,
blocks: vec![blocks],
path: String::new(),
};
@@ -689,9 +644,10 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
shared.fs.write(final_file, v)?;
// Generating settings page.
- page.title = "Rustdoc settings";
+ page.title = "Settings";
page.description = "Settings of Rustdoc";
page.root_path = "./";
+ page.rust_logo = true;
let sidebar = "<h2 class=\"location\">Settings</h2><div class=\"sidebar-elems\"></div>";
let v = layout::render(
@@ -739,9 +695,10 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
shared.fs.write(settings_file, v)?;
// Generating help page.
- page.title = "Rustdoc help";
+ page.title = "Help";
page.description = "Documentation for Rustdoc";
page.root_path = "./";
+ page.rust_logo = true;
let sidebar = "<h2 class=\"location\">Help</h2><div class=\"sidebar-elems\"></div>";
let v = layout::render(
diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs
index 3e671a64b..c52fa01bd 100644
--- a/src/librustdoc/html/render/mod.rs
+++ b/src/librustdoc/html/render/mod.rs
@@ -48,13 +48,13 @@ use std::str;
use std::string::ToString;
use askama::Template;
-use rustc_attr::{ConstStability, Deprecation, StabilityLevel};
+use rustc_attr::{ConstStability, DeprecatedSince, Deprecation, StabilityLevel, StableSince};
use rustc_data_structures::captures::Captures;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_hir::def_id::{DefId, DefIdSet};
use rustc_hir::Mutability;
-use rustc_middle::middle::stability;
-use rustc_middle::ty::TyCtxt;
+use rustc_middle::ty::{self, TyCtxt};
+use rustc_session::RustcVersion;
use rustc_span::{
symbol::{sym, Symbol},
BytePos, FileName, RealFileName,
@@ -102,6 +102,7 @@ pub(crate) struct IndexItem {
pub(crate) desc: String,
pub(crate) parent: Option<DefId>,
pub(crate) parent_idx: Option<isize>,
+ pub(crate) impl_id: Option<DefId>,
pub(crate) search_type: Option<IndexItemFunctionType>,
pub(crate) aliases: Box<[Symbol]>,
pub(crate) deprecation: Option<Deprecation>,
@@ -615,24 +616,22 @@ fn short_item_info(
) -> Vec<ShortItemInfo> {
let mut extra_info = vec![];
- if let Some(depr @ Deprecation { note, since, is_since_rustc_version: _, suggestion: _ }) =
- item.deprecation(cx.tcx())
- {
+ if let Some(depr @ Deprecation { note, since, suggestion: _ }) = item.deprecation(cx.tcx()) {
// We display deprecation messages for #[deprecated], but only display
// the future-deprecation messages for rustc versions.
- let mut message = if let Some(since) = since {
- let since = since.as_str();
- if !stability::deprecation_in_effect(&depr) {
- if since == "TBD" {
- String::from("Deprecating in a future Rust version")
+ let mut message = match since {
+ DeprecatedSince::RustcVersion(version) => {
+ if depr.is_in_effect() {
+ format!("Deprecated since {version}")
} else {
- format!("Deprecating in {}", Escape(since))
+ format!("Deprecating in {version}")
}
- } else {
- format!("Deprecated since {}", Escape(since))
}
- } else {
- String::from("Deprecated")
+ DeprecatedSince::Future => String::from("Deprecating in a future Rust version"),
+ DeprecatedSince::NonStandard(since) => {
+ format!("Deprecated since {}", Escape(since.as_str()))
+ }
+ DeprecatedSince::Unspecified | DeprecatedSince::Err => String::from("Deprecated"),
};
if let Some(note) = note {
@@ -867,10 +866,10 @@ fn assoc_method(
let (indent, indent_str, end_newline) = if parent == ItemType::Trait {
header_len += 4;
let indent_str = " ";
- write!(w, "{}", render_attributes_in_pre(meth, indent_str, tcx));
+ write!(w, "{}", render_attributes_in_pre(meth, indent_str, cx));
(4, indent_str, Ending::NoNewline)
} else {
- render_attributes_in_code(w, meth, tcx);
+ render_attributes_in_code(w, meth, cx);
(0, "", Ending::Newline)
};
w.reserve(header_len + "<a href=\"\" class=\"fn\">{".len() + "</a>".len());
@@ -910,13 +909,17 @@ fn assoc_method(
/// consequence of the above rules.
fn render_stability_since_raw_with_extra(
w: &mut Buffer,
- ver: Option<Symbol>,
+ ver: Option<StableSince>,
const_stability: Option<ConstStability>,
- containing_ver: Option<Symbol>,
- containing_const_ver: Option<Symbol>,
+ containing_ver: Option<StableSince>,
+ containing_const_ver: Option<StableSince>,
extra_class: &str,
) -> bool {
- let stable_version = ver.filter(|inner| !inner.is_empty() && Some(*inner) != containing_ver);
+ let stable_version = if ver != containing_ver && let Some(ver) = &ver {
+ since_to_string(ver)
+ } else {
+ None
+ };
let mut title = String::new();
let mut stability = String::new();
@@ -930,7 +933,8 @@ fn render_stability_since_raw_with_extra(
Some(ConstStability { level: StabilityLevel::Stable { since, .. }, .. })
if Some(since) != containing_const_ver =>
{
- Some((format!("const since {since}"), format!("const: {since}")))
+ since_to_string(&since)
+ .map(|since| (format!("const since {since}"), format!("const: {since}")))
}
Some(ConstStability { level: StabilityLevel::Unstable { issue, .. }, feature, .. }) => {
let unstable = if let Some(n) = issue {
@@ -970,13 +974,21 @@ fn render_stability_since_raw_with_extra(
!stability.is_empty()
}
+fn since_to_string(since: &StableSince) -> Option<String> {
+ match since {
+ StableSince::Version(since) => Some(since.to_string()),
+ StableSince::Current => Some(RustcVersion::CURRENT.to_string()),
+ StableSince::Err => None,
+ }
+}
+
#[inline]
fn render_stability_since_raw(
w: &mut Buffer,
- ver: Option<Symbol>,
+ ver: Option<StableSince>,
const_stability: Option<ConstStability>,
- containing_ver: Option<Symbol>,
- containing_const_ver: Option<Symbol>,
+ containing_ver: Option<StableSince>,
+ containing_const_ver: Option<StableSince>,
) -> bool {
render_stability_since_raw_with_extra(
w,
@@ -1046,13 +1058,13 @@ fn render_assoc_item(
// When an attribute is rendered inside a `<pre>` tag, it is formatted using
// a whitespace prefix and newline.
-fn render_attributes_in_pre<'a, 'b: 'a>(
+fn render_attributes_in_pre<'a, 'tcx: 'a>(
it: &'a clean::Item,
prefix: &'a str,
- tcx: TyCtxt<'b>,
-) -> impl fmt::Display + Captures<'a> + Captures<'b> {
+ cx: &'a Context<'tcx>,
+) -> impl fmt::Display + Captures<'a> + Captures<'tcx> {
crate::html::format::display_fn(move |f| {
- for a in it.attributes(tcx, false) {
+ for a in it.attributes(cx.tcx(), cx.cache(), false) {
writeln!(f, "{prefix}{a}")?;
}
Ok(())
@@ -1061,8 +1073,8 @@ fn render_attributes_in_pre<'a, 'b: 'a>(
// When an attribute is rendered inside a <code> tag, it is formatted using
// a div to produce a newline after it.
-fn render_attributes_in_code(w: &mut impl fmt::Write, it: &clean::Item, tcx: TyCtxt<'_>) {
- for attr in it.attributes(tcx, false) {
+fn render_attributes_in_code(w: &mut impl fmt::Write, it: &clean::Item, cx: &Context<'_>) {
+ for attr in it.attributes(cx.tcx(), cx.cache(), false) {
write!(w, "<div class=\"code-attribute\">{attr}</div>").unwrap();
}
}
@@ -1131,13 +1143,13 @@ pub(crate) fn render_all_impls(
fn render_assoc_items<'a, 'cx: 'a>(
cx: &'a mut Context<'cx>,
containing_item: &'a clean::Item,
- did: DefId,
+ it: DefId,
what: AssocItemRender<'a>,
) -> impl fmt::Display + 'a + Captures<'cx> {
let mut derefs = DefIdSet::default();
- derefs.insert(did);
+ derefs.insert(it);
display_fn(move |f| {
- render_assoc_items_inner(f, cx, containing_item, did, what, &mut derefs);
+ render_assoc_items_inner(f, cx, containing_item, it, what, &mut derefs);
Ok(())
})
}
@@ -1146,17 +1158,15 @@ fn render_assoc_items_inner(
mut w: &mut dyn fmt::Write,
cx: &mut Context<'_>,
containing_item: &clean::Item,
- did: DefId,
+ it: DefId,
what: AssocItemRender<'_>,
derefs: &mut DefIdSet,
) {
info!("Documenting associated items of {:?}", containing_item.name);
let shared = Rc::clone(&cx.shared);
- let v = shared.all_impls_for_item(containing_item, did);
- let v = v.as_slice();
- let (non_trait, traits): (Vec<&Impl>, _) =
- v.iter().partition(|i| i.inner_impl().trait_.is_none());
- let mut saw_impls = FxHashSet::default();
+ let cache = &shared.cache;
+ let Some(v) = cache.impls.get(&it) else { return };
+ let (non_trait, traits): (Vec<_>, _) = v.iter().partition(|i| i.inner_impl().trait_.is_none());
if !non_trait.is_empty() {
let mut tmp_buf = Buffer::html();
let (render_mode, id, class_html) = match what {
@@ -1185,9 +1195,6 @@ fn render_assoc_items_inner(
};
let mut impls_buf = Buffer::html();
for i in &non_trait {
- if !saw_impls.insert(i.def_id()) {
- continue;
- }
render_impl(
&mut impls_buf,
cx,
@@ -1233,10 +1240,8 @@ fn render_assoc_items_inner(
let (synthetic, concrete): (Vec<&Impl>, Vec<&Impl>) =
traits.into_iter().partition(|t| t.inner_impl().kind.is_auto());
- let (blanket_impl, concrete): (Vec<&Impl>, _) = concrete
- .into_iter()
- .filter(|t| saw_impls.insert(t.def_id()))
- .partition(|t| t.inner_impl().kind.is_blanket());
+ let (blanket_impl, concrete): (Vec<&Impl>, _) =
+ concrete.into_iter().partition(|t| t.inner_impl().kind.is_blanket());
render_all_impls(w, cx, containing_item, &concrete, &synthetic, &blanket_impl);
}
@@ -1877,7 +1882,7 @@ pub(crate) fn render_impl_summary(
aliases: &[String],
) {
let inner_impl = i.inner_impl();
- let id = cx.derive_id(get_id_for_impl(&inner_impl.for_, inner_impl.trait_.as_ref(), cx));
+ let id = cx.derive_id(get_id_for_impl(cx.tcx(), i.impl_item.item_id));
let aliases = if aliases.is_empty() {
String::new()
} else {
@@ -1994,21 +1999,35 @@ pub(crate) fn small_url_encode(s: String) -> String {
}
}
-fn get_id_for_impl(for_: &clean::Type, trait_: Option<&clean::Path>, cx: &Context<'_>) -> String {
- match trait_ {
- Some(t) => small_url_encode(format!("impl-{:#}-for-{:#}", t.print(cx), for_.print(cx))),
- None => small_url_encode(format!("impl-{:#}", for_.print(cx))),
- }
+fn get_id_for_impl<'tcx>(tcx: TyCtxt<'tcx>, impl_id: ItemId) -> String {
+ use rustc_middle::ty::print::with_forced_trimmed_paths;
+ let (type_, trait_) = match impl_id {
+ ItemId::Auto { trait_, for_ } => {
+ let ty = tcx.type_of(for_).skip_binder();
+ (ty, Some(ty::TraitRef::new(tcx, trait_, [ty])))
+ }
+ ItemId::Blanket { impl_id, .. } | ItemId::DefId(impl_id) => {
+ match tcx.impl_subject(impl_id).skip_binder() {
+ ty::ImplSubject::Trait(trait_ref) => {
+ (trait_ref.args[0].expect_ty(), Some(trait_ref))
+ }
+ ty::ImplSubject::Inherent(ty) => (ty, None),
+ }
+ }
+ };
+ with_forced_trimmed_paths!(small_url_encode(if let Some(trait_) = trait_ {
+ format!("impl-{trait_}-for-{type_}", trait_ = trait_.print_only_trait_path())
+ } else {
+ format!("impl-{type_}")
+ }))
}
fn extract_for_impl_name(item: &clean::Item, cx: &Context<'_>) -> Option<(String, String)> {
match *item.kind {
- clean::ItemKind::ImplItem(ref i) => {
- i.trait_.as_ref().map(|trait_| {
- // Alternative format produces no URLs,
- // so this parameter does nothing.
- (format!("{:#}", i.for_.print(cx)), get_id_for_impl(&i.for_, Some(trait_), cx))
- })
+ clean::ItemKind::ImplItem(ref i) if i.trait_.is_some() => {
+ // Alternative format produces no URLs,
+ // so this parameter does nothing.
+ Some((format!("{:#}", i.for_.print(cx)), get_id_for_impl(cx.tcx(), item.item_id)))
}
_ => None,
}
@@ -2079,6 +2098,7 @@ impl ItemSection {
const ALL: &'static [Self] = {
use ItemSection::*;
// NOTE: The order here affects the order in the UI.
+ // Keep this synchronized with addSidebarItems in main.js
&[
Reexports,
PrimitiveTypes,
diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs
index c6751c958..d226701ba 100644
--- a/src/librustdoc/html/render/print_item.rs
+++ b/src/librustdoc/html/render/print_item.rs
@@ -5,10 +5,12 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_hir as hir;
use rustc_hir::def::CtorKind;
use rustc_hir::def_id::DefId;
-use rustc_middle::middle::stability;
+use rustc_index::IndexVec;
+use rustc_middle::query::Key;
use rustc_middle::ty::{self, TyCtxt};
use rustc_span::hygiene::MacroKind;
use rustc_span::symbol::{kw, sym, Symbol};
+use rustc_target::abi::VariantIdx;
use std::cell::{RefCell, RefMut};
use std::cmp::Ordering;
use std::fmt;
@@ -117,8 +119,7 @@ macro_rules! item_template_methods {
fn render_attributes_in_pre<'b>(&'b self) -> impl fmt::Display + Captures<'a> + 'b + Captures<'cx> {
display_fn(move |f| {
let (item, cx) = self.item_and_mut_cx();
- let tcx = cx.tcx();
- let v = render_attributes_in_pre(item, "", tcx);
+ let v = render_attributes_in_pre(item, "", &cx);
write!(f, "{v}")
})
}
@@ -589,11 +590,7 @@ fn extra_info_tags<'a, 'tcx: 'a>(
// The trailing space after each tag is to space it properly against the rest of the docs.
if let Some(depr) = &item.deprecation(tcx) {
- let message = if stability::deprecation_in_effect(depr) {
- "Deprecated"
- } else {
- "Deprecation planned"
- };
+ let message = if depr.is_in_effect() { "Deprecated" } else { "Deprecation planned" };
write!(f, "{}", tag_html("deprecated", "", message))?;
}
@@ -656,7 +653,7 @@ fn item_function(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, f: &cle
w,
"{attrs}{vis}{constness}{asyncness}{unsafety}{abi}fn \
{name}{generics}{decl}{notable_traits}{where_clause}",
- attrs = render_attributes_in_pre(it, "", tcx),
+ attrs = render_attributes_in_pre(it, "", cx),
vis = visibility,
constness = constness,
asyncness = asyncness,
@@ -691,7 +688,7 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean:
write!(
w,
"{attrs}{vis}{unsafety}{is_auto}trait {name}{generics}{bounds}",
- attrs = render_attributes_in_pre(it, "", tcx),
+ attrs = render_attributes_in_pre(it, "", cx),
vis = visibility_print_with_space(it.visibility(tcx), it.item_id, cx),
unsafety = t.unsafety(tcx).print_with_space(),
is_auto = if t.is_auto(tcx) { "auto " } else { "" },
@@ -957,6 +954,21 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean:
let cloned_shared = Rc::clone(&cx.shared);
let cache = &cloned_shared.cache;
let mut extern_crates = FxHashSet::default();
+
+ if !t.is_object_safe(cx.tcx()) {
+ write_small_section_header(
+ w,
+ "object-safety",
+ "Object Safety",
+ &format!(
+ "<div class=\"object-safety-info\">This trait is <b>not</b> \
+ <a href=\"{base}/reference/items/traits.html#object-safety\">\
+ object safe</a>.</div>",
+ base = crate::clean::utils::DOC_RUST_LANG_ORG_CHANNEL
+ ),
+ );
+ }
+
if let Some(implementors) = cache.implementors.get(&it.item_id.expect_def_id()) {
// The DefId is for the first Type found with that name. The bool is
// if any Types with the same name but different DefId have been found.
@@ -979,7 +991,7 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean:
}
}
- let (local, foreign) =
+ let (local, mut foreign) =
implementors.iter().partition::<Vec<_>, _>(|i| i.is_on_local_type(cx));
let (mut synthetic, mut concrete): (Vec<&&Impl>, Vec<&&Impl>) =
@@ -987,6 +999,7 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean:
synthetic.sort_by_cached_key(|i| ImplString::new(i, cx));
concrete.sort_by_cached_key(|i| ImplString::new(i, cx));
+ foreign.sort_by_cached_key(|i| ImplString::new(i, cx));
if !foreign.is_empty() {
write_small_section_header(w, "foreign-impls", "Implementations on Foreign Types", "");
@@ -1064,6 +1077,8 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean:
}
}
+ // [RUSTDOCIMPL] trait.impl
+ //
// Include implementors in crates that depend on the current crate.
//
// This is complicated by the way rustdoc is invoked, which is basically
@@ -1099,7 +1114,7 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean:
// ```
//
// Basically, we want `C::Baz` and `A::Foo` to show the same set of
- // impls, which is easier if they both treat `/implementors/A/trait.Foo.js`
+ // impls, which is easier if they both treat `/trait.impl/A/trait.Foo.js`
// as the Single Source of Truth.
//
// We also want the `impl Baz for Quux` to be written to
@@ -1108,7 +1123,7 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean:
// because that'll load faster, and it's better for SEO. And we don't want
// the same impl to show up twice on the same page.
//
- // To make this work, the implementors JS file has a structure kinda
+ // To make this work, the trait.impl/A/trait.Foo.js JS file has a structure kinda
// like this:
//
// ```js
@@ -1125,7 +1140,7 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean:
// So C's HTML will have something like this:
//
// ```html
- // <script src="/implementors/A/trait.Foo.js"
+ // <script src="/trait.impl/A/trait.Foo.js"
// data-ignore-extern-crates="A,B" async></script>
// ```
//
@@ -1135,7 +1150,7 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean:
// [JSONP]: https://en.wikipedia.org/wiki/JSONP
let mut js_src_path: UrlPartsBuilder = std::iter::repeat("..")
.take(cx.current.len())
- .chain(std::iter::once("implementors"))
+ .chain(std::iter::once("trait.impl"))
.collect();
if let Some(did) = it.item_id.as_def_id() &&
let get_extern = { || cache.external_paths.get(&did).map(|s| &s.0) } &&
@@ -1170,7 +1185,7 @@ fn item_trait_alias(
write!(
w,
"{attrs}trait {name}{generics}{where_b} = {bounds};",
- attrs = render_attributes_in_pre(it, "", cx.tcx()),
+ attrs = render_attributes_in_pre(it, "", cx),
name = it.name.unwrap(),
generics = t.generics.print(cx),
where_b = print_where_clause(&t.generics, cx, 0, Ending::Newline),
@@ -1198,7 +1213,7 @@ fn item_opaque_ty(
write!(
w,
"{attrs}type {name}{generics}{where_clause} = impl {bounds};",
- attrs = render_attributes_in_pre(it, "", cx.tcx()),
+ attrs = render_attributes_in_pre(it, "", cx),
name = it.name.unwrap(),
generics = t.generics.print(cx),
where_clause = print_where_clause(&t.generics, cx, 0, Ending::Newline),
@@ -1223,7 +1238,7 @@ fn item_type_alias(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &c
write!(
w,
"{attrs}{vis}type {name}{generics}{where_clause} = {type_};",
- attrs = render_attributes_in_pre(it, "", cx.tcx()),
+ attrs = render_attributes_in_pre(it, "", cx),
vis = visibility_print_with_space(it.visibility(cx.tcx()), it.item_id, cx),
name = it.name.unwrap(),
generics = t.generics.print(cx),
@@ -1247,6 +1262,9 @@ fn item_type_alias(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &c
match inner_type {
clean::TypeAliasInnerType::Enum { variants, is_non_exhaustive } => {
let variants_iter = || variants.iter().filter(|i| !i.is_stripped());
+ let ty = cx.tcx().type_of(it.def_id().unwrap()).instantiate_identity();
+ let enum_def_id = ty.ty_adt_id().unwrap();
+
wrap_item(w, |w| {
let variants_len = variants.len();
let variants_count = variants_iter().count();
@@ -1257,13 +1275,14 @@ fn item_type_alias(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &c
w,
cx,
Some(&t.generics),
- variants_iter(),
+ &variants,
variants_count,
has_stripped_entries,
*is_non_exhaustive,
+ enum_def_id,
)
});
- item_variants(w, cx, it, variants_iter());
+ item_variants(w, cx, it, &variants, enum_def_id);
}
clean::TypeAliasInnerType::Union { fields } => {
wrap_item(w, |w| {
@@ -1313,6 +1332,102 @@ fn item_type_alias(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &c
// we need #14072 to make sense of the generics.
write!(w, "{}", render_assoc_items(cx, it, def_id, AssocItemRender::All));
write!(w, "{}", document_type_layout(cx, def_id));
+
+ // [RUSTDOCIMPL] type.impl
+ //
+ // Include type definitions from the alias target type.
+ //
+ // Earlier versions of this code worked by having `render_assoc_items`
+ // include this data directly. That generates *O*`(types*impls)` of HTML
+ // text, and some real crates have a lot of types and impls.
+ //
+ // To create the same UX without generating half a gigabyte of HTML for a
+ // crate that only contains 20 megabytes of actual documentation[^115718],
+ // rustdoc stashes these type-alias-inlined docs in a [JSONP]
+ // "database-lite". The file itself is generated in `write_shared.rs`,
+ // and hooks into functions provided by `main.js`.
+ //
+ // The format of `trait.impl` and `type.impl` JS files are superficially
+ // similar. Each line, except the JSONP wrapper itself, belongs to a crate,
+ // and they are otherwise separate (rustdoc should be idempotent). The
+ // "meat" of the file is HTML strings, so the frontend code is very simple.
+ // Links are relative to the doc root, though, so the frontend needs to fix
+ // that up, and inlined docs can reuse these files.
+ //
+ // However, there are a few differences, caused by the sophisticated
+ // features that type aliases have. Consider this crate graph:
+ //
+ // ```text
+ // ---------------------------------
+ // | crate A: struct Foo<T> |
+ // | type Bar = Foo<i32> |
+ // | impl X for Foo<i8> |
+ // | impl Y for Foo<i32> |
+ // ---------------------------------
+ // |
+ // ----------------------------------
+ // | crate B: type Baz = A::Foo<i8> |
+ // | type Xyy = A::Foo<i8> |
+ // | impl Z for Xyy |
+ // ----------------------------------
+ // ```
+ //
+ // The type.impl/A/struct.Foo.js JS file has a structure kinda like this:
+ //
+ // ```js
+ // JSONP({
+ // "A": [["impl Y for Foo<i32>", "Y", "A::Bar"]],
+ // "B": [["impl X for Foo<i8>", "X", "B::Baz", "B::Xyy"], ["impl Z for Xyy", "Z", "B::Baz"]],
+ // });
+ // ```
+ //
+ // When the type.impl file is loaded, only the current crate's docs are
+ // actually used. The main reason to bundle them together is that there's
+ // enough duplication in them for DEFLATE to remove the redundancy.
+ //
+ // The contents of a crate are a list of impl blocks, themselves
+ // represented as lists. The first item in the sublist is the HTML block,
+ // the second item is the name of the trait (which goes in the sidebar),
+ // and all others are the names of type aliases that successfully match.
+ //
+ // This way:
+ //
+ // - There's no need to generate these files for types that have no aliases
+ // in the current crate. If a dependent crate makes a type alias, it'll
+ // take care of generating its own docs.
+ // - There's no need to reimplement parts of the type checker in
+ // JavaScript. The Rust backend does the checking, and includes its
+ // results in the file.
+ // - Docs defined directly on the type alias are dropped directly in the
+ // HTML by `render_assoc_items`, and are accessible without JavaScript.
+ // The JSONP file will not list impl items that are known to be part
+ // of the main HTML file already.
+ //
+ // [JSONP]: https://en.wikipedia.org/wiki/JSONP
+ // [^115718]: https://github.com/rust-lang/rust/issues/115718
+ let cloned_shared = Rc::clone(&cx.shared);
+ let cache = &cloned_shared.cache;
+ if let Some(target_did) = t.type_.def_id(cache) &&
+ let get_extern = { || cache.external_paths.get(&target_did) } &&
+ let Some(&(ref target_fqp, target_type)) = cache.paths.get(&target_did).or_else(get_extern) &&
+ target_type.is_adt() && // primitives cannot be inlined
+ let Some(self_did) = it.item_id.as_def_id() &&
+ let get_local = { || cache.paths.get(&self_did).map(|(p, _)| p) } &&
+ let Some(self_fqp) = cache.exact_paths.get(&self_did).or_else(get_local)
+ {
+ let mut js_src_path: UrlPartsBuilder = std::iter::repeat("..")
+ .take(cx.current.len())
+ .chain(std::iter::once("type.impl"))
+ .collect();
+ js_src_path.extend(target_fqp[..target_fqp.len() - 1].iter().copied());
+ js_src_path.push_fmt(format_args!("{target_type}.{}.js", target_fqp.last().unwrap()));
+ let self_path = self_fqp.iter().map(Symbol::as_str).collect::<Vec<&str>>().join("::");
+ write!(
+ w,
+ "<script src=\"{src}\" data-self-path=\"{self_path}\" async></script>",
+ src = js_src_path.finish(),
+ );
+ }
}
fn item_union(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean::Union) {
@@ -1408,7 +1523,7 @@ fn item_enum(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, e: &clean::
let tcx = cx.tcx();
let count_variants = e.variants().count();
wrap_item(w, |w| {
- render_attributes_in_code(w, it, tcx);
+ render_attributes_in_code(w, it, cx);
write!(
w,
"{}enum {}{}",
@@ -1416,36 +1531,90 @@ fn item_enum(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, e: &clean::
it.name.unwrap(),
e.generics.print(cx),
);
+
render_enum_fields(
w,
cx,
Some(&e.generics),
- e.variants(),
+ &e.variants,
count_variants,
e.has_stripped_entries(),
it.is_non_exhaustive(),
+ it.def_id().unwrap(),
);
});
write!(w, "{}", document(cx, it, None, HeadingOffset::H2));
if count_variants != 0 {
- item_variants(w, cx, it, e.variants());
+ item_variants(w, cx, it, &e.variants, it.def_id().unwrap());
}
let def_id = it.item_id.expect_def_id();
write!(w, "{}", render_assoc_items(cx, it, def_id, AssocItemRender::All));
write!(w, "{}", document_type_layout(cx, def_id));
}
-fn render_enum_fields<'a>(
+/// It'll return false if any variant is not a C-like variant. Otherwise it'll return true if at
+/// least one of them has an explicit discriminant or if the enum has `#[repr(C)]` or an integer
+/// `repr`.
+fn should_show_enum_discriminant(
+ cx: &Context<'_>,
+ enum_def_id: DefId,
+ variants: &IndexVec<VariantIdx, clean::Item>,
+) -> bool {
+ let mut has_variants_with_value = false;
+ for variant in variants {
+ if let clean::VariantItem(ref var) = *variant.kind &&
+ matches!(var.kind, clean::VariantKind::CLike)
+ {
+ has_variants_with_value |= var.discriminant.is_some();
+ } else {
+ return false;
+ }
+ }
+ if has_variants_with_value {
+ return true;
+ }
+ let repr = cx.tcx().adt_def(enum_def_id).repr();
+ repr.c() || repr.int.is_some()
+}
+
+fn display_c_like_variant(
+ w: &mut Buffer,
+ cx: &mut Context<'_>,
+ item: &clean::Item,
+ variant: &clean::Variant,
+ index: VariantIdx,
+ should_show_enum_discriminant: bool,
+ enum_def_id: DefId,
+) {
+ let name = item.name.unwrap();
+ if let Some(ref value) = variant.discriminant {
+ write!(w, "{} = {}", name.as_str(), value.value(cx.tcx(), true));
+ } else if should_show_enum_discriminant {
+ let adt_def = cx.tcx().adt_def(enum_def_id);
+ let discr = adt_def.discriminant_for_variant(cx.tcx(), index);
+ if discr.ty.is_signed() {
+ write!(w, "{} = {}", name.as_str(), discr.val as i128);
+ } else {
+ write!(w, "{} = {}", name.as_str(), discr.val);
+ }
+ } else {
+ w.write_str(name.as_str());
+ }
+}
+
+fn render_enum_fields(
mut w: &mut Buffer,
cx: &mut Context<'_>,
g: Option<&clean::Generics>,
- variants: impl Iterator<Item = &'a clean::Item>,
+ variants: &IndexVec<VariantIdx, clean::Item>,
count_variants: usize,
has_stripped_entries: bool,
is_non_exhaustive: bool,
+ enum_def_id: DefId,
) {
+ let should_show_enum_discriminant = should_show_enum_discriminant(cx, enum_def_id, variants);
if !g.is_some_and(|g| print_where_clause_and_check(w, g, cx)) {
// If there wasn't a `where` clause, we add a whitespace.
w.write_str(" ");
@@ -1461,15 +1630,24 @@ fn render_enum_fields<'a>(
toggle_open(&mut w, format_args!("{count_variants} variants"));
}
const TAB: &str = " ";
- for v in variants {
+ for (index, v) in variants.iter_enumerated() {
+ if v.is_stripped() {
+ continue;
+ }
w.write_str(TAB);
- let name = v.name.unwrap();
match *v.kind {
- // FIXME(#101337): Show discriminant
clean::VariantItem(ref var) => match var.kind {
- clean::VariantKind::CLike => w.write_str(name.as_str()),
+ clean::VariantKind::CLike => display_c_like_variant(
+ w,
+ cx,
+ v,
+ var,
+ index,
+ should_show_enum_discriminant,
+ enum_def_id,
+ ),
clean::VariantKind::Tuple(ref s) => {
- write!(w, "{name}({})", print_tuple_struct_fields(cx, s),);
+ write!(w, "{}({})", v.name.unwrap(), print_tuple_struct_fields(cx, s));
}
clean::VariantKind::Struct(ref s) => {
render_struct(w, v, None, None, &s.fields, TAB, false, cx);
@@ -1490,11 +1668,12 @@ fn render_enum_fields<'a>(
}
}
-fn item_variants<'a>(
+fn item_variants(
w: &mut Buffer,
cx: &mut Context<'_>,
it: &clean::Item,
- variants: impl Iterator<Item = &'a clean::Item>,
+ variants: &IndexVec<VariantIdx, clean::Item>,
+ enum_def_id: DefId,
) {
let tcx = cx.tcx();
write!(
@@ -1507,7 +1686,11 @@ fn item_variants<'a>(
document_non_exhaustive_header(it),
document_non_exhaustive(it)
);
- for variant in variants {
+ let should_show_enum_discriminant = should_show_enum_discriminant(cx, enum_def_id, variants);
+ for (index, variant) in variants.iter_enumerated() {
+ if variant.is_stripped() {
+ continue;
+ }
let id = cx.derive_id(format!("{}.{}", ItemType::Variant, variant.name.unwrap()));
write!(
w,
@@ -1522,7 +1705,22 @@ fn item_variants<'a>(
it.const_stable_since(tcx),
" rightside",
);
- write!(w, "<h3 class=\"code-header\">{name}", name = variant.name.unwrap());
+ w.write_str("<h3 class=\"code-header\">");
+ if let clean::VariantItem(ref var) = *variant.kind &&
+ let clean::VariantKind::CLike = var.kind
+ {
+ display_c_like_variant(
+ w,
+ cx,
+ variant,
+ var,
+ index,
+ should_show_enum_discriminant,
+ enum_def_id,
+ );
+ } else {
+ w.write_str(variant.name.unwrap().as_str());
+ }
let clean::VariantItem(variant_data) = &*variant.kind else { unreachable!() };
@@ -1644,7 +1842,7 @@ fn item_primitive(w: &mut impl fmt::Write, cx: &mut Context<'_>, it: &clean::Ite
fn item_constant(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, c: &clean::Constant) {
wrap_item(w, |w| {
let tcx = cx.tcx();
- render_attributes_in_code(w, it, tcx);
+ render_attributes_in_code(w, it, cx);
write!(
w,
@@ -1693,7 +1891,7 @@ fn item_constant(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, c: &cle
fn item_struct(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean::Struct) {
wrap_item(w, |w| {
- render_attributes_in_code(w, it, cx.tcx());
+ render_attributes_in_code(w, it, cx);
render_struct(w, it, Some(&s.generics), s.ctor_kind, &s.fields, "", true, cx);
});
@@ -1753,7 +1951,7 @@ fn item_fields(
fn item_static(w: &mut impl fmt::Write, cx: &mut Context<'_>, it: &clean::Item, s: &clean::Static) {
wrap_item(w, |buffer| {
- render_attributes_in_code(buffer, it, cx.tcx());
+ render_attributes_in_code(buffer, it, cx);
write!(
buffer,
"{vis}static {mutability}{name}: {typ}",
@@ -1771,7 +1969,7 @@ fn item_static(w: &mut impl fmt::Write, cx: &mut Context<'_>, it: &clean::Item,
fn item_foreign_type(w: &mut impl fmt::Write, cx: &mut Context<'_>, it: &clean::Item) {
wrap_item(w, |buffer| {
buffer.write_str("extern {\n").unwrap();
- render_attributes_in_code(buffer, it, cx.tcx());
+ render_attributes_in_code(buffer, it, cx);
write!(
buffer,
" {}type {};\n}}",
diff --git a/src/librustdoc/html/render/search_index.rs b/src/librustdoc/html/render/search_index.rs
index 78c443b22..af1dab594 100644
--- a/src/librustdoc/html/render/search_index.rs
+++ b/src/librustdoc/html/render/search_index.rs
@@ -12,7 +12,7 @@ use crate::formats::cache::{Cache, OrphanImplItem};
use crate::formats::item_type::ItemType;
use crate::html::format::join_with_double_colon;
use crate::html::markdown::short_markdown_summary;
-use crate::html::render::{IndexItem, IndexItemFunctionType, RenderType, RenderTypeId};
+use crate::html::render::{self, IndexItem, IndexItemFunctionType, RenderType, RenderTypeId};
/// Builds the search index from the collected metadata
pub(crate) fn build_index<'tcx>(
@@ -26,7 +26,8 @@ pub(crate) fn build_index<'tcx>(
// Attach all orphan items to the type's definition if the type
// has since been learned.
- for &OrphanImplItem { parent, ref item, ref impl_generics } in &cache.orphan_impl_items {
+ for &OrphanImplItem { impl_id, parent, ref item, ref impl_generics } in &cache.orphan_impl_items
+ {
if let Some((fqp, _)) = cache.paths.get(&parent) {
let desc = short_markdown_summary(&item.doc_value(), &item.link_names(cache));
cache.search_index.push(IndexItem {
@@ -36,6 +37,7 @@ pub(crate) fn build_index<'tcx>(
desc,
parent: Some(parent),
parent_idx: None,
+ impl_id,
search_type: get_function_type_for_search(item, tcx, impl_generics.as_ref(), cache),
aliases: item.attrs.get_doc_aliases(),
deprecation: item.deprecation(tcx),
@@ -222,6 +224,29 @@ pub(crate) fn build_index<'tcx>(
})
.collect();
+ // Find associated items that need disambiguators
+ let mut associated_item_duplicates = FxHashMap::<(isize, ItemType, Symbol), usize>::default();
+
+ for &item in &crate_items {
+ if item.impl_id.is_some() && let Some(parent_idx) = item.parent_idx {
+ let count = associated_item_duplicates
+ .entry((parent_idx, item.ty, item.name))
+ .or_insert(0);
+ *count += 1;
+ }
+ }
+
+ let associated_item_disambiguators = crate_items
+ .iter()
+ .enumerate()
+ .filter_map(|(index, item)| {
+ let impl_id = ItemId::DefId(item.impl_id?);
+ let parent_idx = item.parent_idx?;
+ let count = *associated_item_duplicates.get(&(parent_idx, item.ty, item.name))?;
+ if count > 1 { Some((index, render::get_id_for_impl(tcx, impl_id))) } else { None }
+ })
+ .collect::<Vec<_>>();
+
struct CrateData<'a> {
doc: String,
items: Vec<&'a IndexItem>,
@@ -230,6 +255,8 @@ pub(crate) fn build_index<'tcx>(
//
// To be noted: the `usize` elements are indexes to `items`.
aliases: &'a BTreeMap<String, Vec<usize>>,
+ // Used when a type has more than one impl with an associated item with the same name.
+ associated_item_disambiguators: &'a Vec<(usize, String)>,
}
struct Paths {
@@ -382,6 +409,7 @@ pub(crate) fn build_index<'tcx>(
crate_data.serialize_field("f", &functions)?;
crate_data.serialize_field("c", &deprecated)?;
crate_data.serialize_field("p", &paths)?;
+ crate_data.serialize_field("b", &self.associated_item_disambiguators)?;
if has_aliases {
crate_data.serialize_field("a", &self.aliases)?;
}
@@ -398,6 +426,7 @@ pub(crate) fn build_index<'tcx>(
items: crate_items,
paths: crate_paths,
aliases: &aliases,
+ associated_item_disambiguators: &associated_item_disambiguators,
})
.expect("failed serde conversion")
// All these `replace` calls are because we have to go through JS string for JSON content.
diff --git a/src/librustdoc/html/render/sidebar.rs b/src/librustdoc/html/render/sidebar.rs
index 76f63c6f6..ba4aaaff5 100644
--- a/src/librustdoc/html/render/sidebar.rs
+++ b/src/librustdoc/html/render/sidebar.rs
@@ -19,7 +19,7 @@ pub(super) struct Sidebar<'a> {
pub(super) title_prefix: &'static str,
pub(super) title: &'a str,
pub(super) is_crate: bool,
- pub(super) version: &'a str,
+ pub(super) is_mod: bool,
pub(super) blocks: Vec<LinkBlock<'a>>,
pub(super) path: String,
}
@@ -38,18 +38,19 @@ pub(crate) struct LinkBlock<'a> {
/// as well as the link to it, e.g. `#implementations`.
/// Will be rendered inside an `<h3>` tag
heading: Link<'a>,
+ class: &'static str,
links: Vec<Link<'a>>,
/// Render the heading even if there are no links
force_render: bool,
}
impl<'a> LinkBlock<'a> {
- pub fn new(heading: Link<'a>, links: Vec<Link<'a>>) -> Self {
- Self { heading, links, force_render: false }
+ pub fn new(heading: Link<'a>, class: &'static str, links: Vec<Link<'a>>) -> Self {
+ Self { heading, links, class, force_render: false }
}
- pub fn forced(heading: Link<'a>) -> Self {
- Self { heading, links: vec![], force_render: true }
+ pub fn forced(heading: Link<'a>, class: &'static str) -> Self {
+ Self { heading, links: vec![], class, force_render: true }
}
pub fn should_render(&self) -> bool {
@@ -99,12 +100,12 @@ pub(super) fn print_sidebar(cx: &Context<'_>, it: &clean::Item, buffer: &mut Buf
|| it.is_primitive()
|| it.is_union()
|| it.is_enum()
- || it.is_mod()
+ // crate title is displayed as part of logo lockup
+ || (it.is_mod() && !it.is_crate())
|| it.is_type_alias()
{
(
match *it.kind {
- clean::ModuleItem(..) if it.is_crate() => "Crate ",
clean::ModuleItem(..) => "Module ",
_ => "",
},
@@ -113,14 +114,22 @@ pub(super) fn print_sidebar(cx: &Context<'_>, it: &clean::Item, buffer: &mut Buf
} else {
("", "")
};
- let version =
- if it.is_crate() { cx.cache().crate_version.as_deref().unwrap_or_default() } else { "" };
- let path: String = if !it.is_mod() {
- cx.current.iter().map(|s| s.as_str()).intersperse("::").collect()
+ // need to show parent path header if:
+ // - it's a child module, instead of the crate root
+ // - there's a sidebar section for the item itself
+ //
+ // otherwise, the parent path header is redundant with the big crate
+ // branding area at the top of the sidebar
+ let sidebar_path =
+ if it.is_mod() { &cx.current[..cx.current.len() - 1] } else { &cx.current[..] };
+ let path: String = if sidebar_path.len() > 1 || !title.is_empty() {
+ let path = sidebar_path.iter().map(|s| s.as_str()).intersperse("::").collect();
+ if sidebar_path.len() == 1 { format!("crate {path}") } else { path }
} else {
"".into()
};
- let sidebar = Sidebar { title_prefix, title, is_crate: it.is_crate(), version, blocks, path };
+ let sidebar =
+ Sidebar { title_prefix, title, is_mod: it.is_mod(), is_crate: it.is_crate(), blocks, path };
sidebar.render_into(buffer).unwrap();
}
@@ -149,7 +158,7 @@ fn sidebar_struct<'a>(
};
let mut items = vec![];
if let Some(name) = field_name {
- items.push(LinkBlock::new(Link::new("fields", name), fields));
+ items.push(LinkBlock::new(Link::new("fields", name), "structfield", fields));
}
sidebar_assoc_items(cx, it, &mut items);
items
@@ -206,12 +215,23 @@ fn sidebar_trait<'a>(
("foreign-impls", "Implementations on Foreign Types", foreign_impls),
]
.into_iter()
- .map(|(id, title, items)| LinkBlock::new(Link::new(id, title), items))
+ .map(|(id, title, items)| LinkBlock::new(Link::new(id, title), "", items))
.collect();
sidebar_assoc_items(cx, it, &mut blocks);
- blocks.push(LinkBlock::forced(Link::new("implementors", "Implementors")));
+
+ if !t.is_object_safe(cx.tcx()) {
+ blocks.push(LinkBlock::forced(
+ Link::new("object-safety", "Object Safety"),
+ "object-safety-note",
+ ));
+ }
+
+ blocks.push(LinkBlock::forced(Link::new("implementors", "Implementors"), "impl"));
if t.is_auto(cx.tcx()) {
- blocks.push(LinkBlock::forced(Link::new("synthetic-implementors", "Auto Implementors")));
+ blocks.push(LinkBlock::forced(
+ Link::new("synthetic-implementors", "Auto Implementors"),
+ "impl-auto",
+ ));
}
blocks
}
@@ -237,7 +257,7 @@ fn sidebar_type_alias<'a>(
) -> Vec<LinkBlock<'a>> {
let mut items = vec![];
if let Some(inner_type) = &t.inner_type {
- items.push(LinkBlock::forced(Link::new("aliased-type", "Aliased type")));
+ items.push(LinkBlock::forced(Link::new("aliased-type", "Aliased type"), "type"));
match inner_type {
clean::TypeAliasInnerType::Enum { variants, is_non_exhaustive: _ } => {
let mut variants = variants
@@ -248,12 +268,12 @@ fn sidebar_type_alias<'a>(
.collect::<Vec<_>>();
variants.sort_unstable();
- items.push(LinkBlock::new(Link::new("variants", "Variants"), variants));
+ items.push(LinkBlock::new(Link::new("variants", "Variants"), "variant", variants));
}
clean::TypeAliasInnerType::Union { fields }
| clean::TypeAliasInnerType::Struct { ctor_kind: _, fields } => {
let fields = get_struct_fields_name(fields);
- items.push(LinkBlock::new(Link::new("fields", "Fields"), fields));
+ items.push(LinkBlock::new(Link::new("fields", "Fields"), "field", fields));
}
}
}
@@ -267,7 +287,7 @@ fn sidebar_union<'a>(
u: &'a clean::Union,
) -> Vec<LinkBlock<'a>> {
let fields = get_struct_fields_name(&u.fields);
- let mut items = vec![LinkBlock::new(Link::new("fields", "Fields"), fields)];
+ let mut items = vec![LinkBlock::new(Link::new("fields", "Fields"), "structfield", fields)];
sidebar_assoc_items(cx, it, &mut items);
items
}
@@ -279,12 +299,11 @@ fn sidebar_assoc_items<'a>(
links: &mut Vec<LinkBlock<'a>>,
) {
let did = it.item_id.expect_def_id();
- let v = cx.shared.all_impls_for_item(it, it.item_id.expect_def_id());
- let v = v.as_slice();
+ let cache = cx.cache();
let mut assoc_consts = Vec::new();
let mut methods = Vec::new();
- if !v.is_empty() {
+ if let Some(v) = cache.impls.get(&did) {
let mut used_links = FxHashSet::default();
let mut id_map = IdMap::new();
@@ -320,7 +339,7 @@ fn sidebar_assoc_items<'a>(
cx,
&mut deref_methods,
impl_,
- v.iter().copied(),
+ v,
&mut derefs,
&mut used_links,
);
@@ -333,12 +352,16 @@ fn sidebar_assoc_items<'a>(
sidebar_render_assoc_items(cx, &mut id_map, concrete, synthetic, blanket_impl)
} else {
- std::array::from_fn(|_| LinkBlock::new(Link::empty(), vec![]))
+ std::array::from_fn(|_| LinkBlock::new(Link::empty(), "", vec![]))
};
let mut blocks = vec![
- LinkBlock::new(Link::new("implementations", "Associated Constants"), assoc_consts),
- LinkBlock::new(Link::new("implementations", "Methods"), methods),
+ LinkBlock::new(
+ Link::new("implementations", "Associated Constants"),
+ "associatedconstant",
+ assoc_consts,
+ ),
+ LinkBlock::new(Link::new("implementations", "Methods"), "method", methods),
];
blocks.append(&mut deref_methods);
blocks.extend([concrete, synthetic, blanket]);
@@ -350,7 +373,7 @@ fn sidebar_deref_methods<'a>(
cx: &'a Context<'_>,
out: &mut Vec<LinkBlock<'a>>,
impl_: &Impl,
- v: impl Iterator<Item = &'a Impl>,
+ v: &[Impl],
derefs: &mut DefIdSet,
used_links: &mut FxHashSet<String>,
) {
@@ -375,7 +398,7 @@ fn sidebar_deref_methods<'a>(
// Avoid infinite cycles
return;
}
- let deref_mut = { v }.any(|i| i.trait_did() == cx.tcx().lang_items().deref_mut_trait());
+ let deref_mut = v.iter().any(|i| i.trait_did() == cx.tcx().lang_items().deref_mut_trait());
let inner_impl = target
.def_id(c)
.or_else(|| {
@@ -407,7 +430,7 @@ fn sidebar_deref_methods<'a>(
);
// We want links' order to be reproducible so we don't use unstable sort.
ret.sort();
- out.push(LinkBlock::new(Link::new(id, title), ret));
+ out.push(LinkBlock::new(Link::new(id, title), "deref-methods", ret));
}
}
@@ -426,7 +449,7 @@ fn sidebar_deref_methods<'a>(
cx,
out,
target_deref_impl,
- target_impls.iter(),
+ target_impls,
derefs,
used_links,
);
@@ -446,7 +469,7 @@ fn sidebar_enum<'a>(
.collect::<Vec<_>>();
variants.sort_unstable();
- let mut items = vec![LinkBlock::new(Link::new("variants", "Variants"), variants)];
+ let mut items = vec![LinkBlock::new(Link::new("variants", "Variants"), "variant", variants)];
sidebar_assoc_items(cx, it, &mut items);
items
}
@@ -460,7 +483,7 @@ pub(crate) fn sidebar_module_like(
.filter(|sec| item_sections_in_use.contains(sec))
.map(|sec| Link::new(sec.id(), sec.name()))
.collect();
- LinkBlock::new(Link::empty(), item_sections)
+ LinkBlock::new(Link::empty(), "", item_sections)
}
fn sidebar_module(items: &[clean::Item]) -> LinkBlock<'static> {
@@ -503,8 +526,7 @@ fn sidebar_render_assoc_items(
.iter()
.filter_map(|it| {
let trait_ = it.inner_impl().trait_.as_ref()?;
- let encoded =
- id_map.derive(super::get_id_for_impl(&it.inner_impl().for_, Some(trait_), cx));
+ let encoded = id_map.derive(super::get_id_for_impl(cx.tcx(), it.impl_item.item_id));
let prefix = match it.inner_impl().polarity {
ty::ImplPolarity::Positive | ty::ImplPolarity::Reservation => "",
@@ -522,12 +544,21 @@ fn sidebar_render_assoc_items(
let synthetic = format_impls(synthetic, id_map);
let blanket = format_impls(blanket_impl, id_map);
[
- LinkBlock::new(Link::new("trait-implementations", "Trait Implementations"), concrete),
+ LinkBlock::new(
+ Link::new("trait-implementations", "Trait Implementations"),
+ "trait-implementation",
+ concrete,
+ ),
LinkBlock::new(
Link::new("synthetic-implementations", "Auto Trait Implementations"),
+ "synthetic-implementation",
synthetic,
),
- LinkBlock::new(Link::new("blanket-implementations", "Blanket Implementations"), blanket),
+ LinkBlock::new(
+ Link::new("blanket-implementations", "Blanket Implementations"),
+ "blanket-implementation",
+ blanket,
+ ),
]
}
diff --git a/src/librustdoc/html/render/write_shared.rs b/src/librustdoc/html/render/write_shared.rs
index e824651e7..d2c7c578c 100644
--- a/src/librustdoc/html/render/write_shared.rs
+++ b/src/librustdoc/html/render/write_shared.rs
@@ -5,18 +5,28 @@ use std::io::{self, BufReader};
use std::path::{Component, Path};
use std::rc::{Rc, Weak};
+use indexmap::IndexMap;
use itertools::Itertools;
use rustc_data_structures::flock;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams};
+use rustc_span::def_id::DefId;
+use rustc_span::Symbol;
use serde::ser::SerializeSeq;
use serde::{Serialize, Serializer};
use super::{collect_paths_for_type, ensure_trailing_slash, Context};
-use crate::clean::Crate;
+use crate::clean::{Crate, Item, ItemId, ItemKind};
use crate::config::{EmitType, RenderOptions};
use crate::docfs::PathError;
use crate::error::Error;
+use crate::formats::cache::Cache;
+use crate::formats::item_type::ItemType;
+use crate::formats::{Impl, RenderMode};
+use crate::html::format::Buffer;
+use crate::html::render::{AssocItemLink, ImplRenderingParameters};
use crate::html::{layout, static_files};
+use crate::visit::DocVisitor;
use crate::{try_err, try_none};
/// Rustdoc writes out two kinds of shared files:
@@ -336,33 +346,286 @@ if (typeof exports !== 'undefined') {exports.searchIndex = searchIndex};
let dst = cx.dst.join("index.html");
let page = layout::Page {
title: "Index of crates",
- css_class: "mod",
+ css_class: "mod sys",
root_path: "./",
static_root_path: shared.static_root_path.as_deref(),
description: "List of crates",
resource_suffix: &shared.resource_suffix,
+ rust_logo: true,
};
let content = format!(
"<h1>List of all crates</h1><ul class=\"all-items\">{}</ul>",
- krates
- .iter()
- .map(|s| {
- format!(
- "<li><a href=\"{trailing_slash}index.html\">{s}</a></li>",
- trailing_slash = ensure_trailing_slash(s),
- )
- })
- .collect::<String>()
+ krates.iter().format_with("", |k, f| {
+ f(&format_args!(
+ "<li><a href=\"{trailing_slash}index.html\">{k}</a></li>",
+ trailing_slash = ensure_trailing_slash(k),
+ ))
+ })
);
let v = layout::render(&shared.layout, &page, "", content, &shared.style_files);
shared.fs.write(dst, v)?;
}
}
+ let cloned_shared = Rc::clone(&cx.shared);
+ let cache = &cloned_shared.cache;
+
+ // Collect the list of aliased types and their aliases.
+ // <https://github.com/search?q=repo%3Arust-lang%2Frust+[RUSTDOCIMPL]+type.impl&type=code>
+ //
+ // The clean AST has type aliases that point at their types, but
+ // this visitor works to reverse that: `aliased_types` is a map
+ // from target to the aliases that reference it, and each one
+ // will generate one file.
+ struct TypeImplCollector<'cx, 'cache> {
+ // Map from DefId-of-aliased-type to its data.
+ aliased_types: IndexMap<DefId, AliasedType<'cache>>,
+ visited_aliases: FxHashSet<DefId>,
+ cache: &'cache Cache,
+ cx: &'cache mut Context<'cx>,
+ }
+ // Data for an aliased type.
+ //
+ // In the final file, the format will be roughly:
+ //
+ // ```json
+ // // type.impl/CRATE/TYPENAME.js
+ // JSONP(
+ // "CRATE": [
+ // ["IMPL1 HTML", "ALIAS1", "ALIAS2", ...],
+ // ["IMPL2 HTML", "ALIAS3", "ALIAS4", ...],
+ // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ struct AliasedType
+ // ...
+ // ]
+ // )
+ // ```
+ struct AliasedType<'cache> {
+ // This is used to generate the actual filename of this aliased type.
+ target_fqp: &'cache [Symbol],
+ target_type: ItemType,
+ // This is the data stored inside the file.
+ // ItemId is used to deduplicate impls.
+ impl_: IndexMap<ItemId, AliasedTypeImpl<'cache>>,
+ }
+ // The `impl_` contains data that's used to figure out if an alias will work,
+ // and to generate the HTML at the end.
+ //
+ // The `type_aliases` list is built up with each type alias that matches.
+ struct AliasedTypeImpl<'cache> {
+ impl_: &'cache Impl,
+ type_aliases: Vec<(&'cache [Symbol], Item)>,
+ }
+ impl<'cx, 'cache> DocVisitor for TypeImplCollector<'cx, 'cache> {
+ fn visit_item(&mut self, it: &Item) {
+ self.visit_item_recur(it);
+ let cache = self.cache;
+ let ItemKind::TypeAliasItem(ref t) = *it.kind else { return };
+ let Some(self_did) = it.item_id.as_def_id() else { return };
+ if !self.visited_aliases.insert(self_did) {
+ return;
+ }
+ let Some(target_did) = t.type_.def_id(cache) else { return };
+ let get_extern = { || cache.external_paths.get(&target_did) };
+ let Some(&(ref target_fqp, target_type)) =
+ cache.paths.get(&target_did).or_else(get_extern)
+ else {
+ return;
+ };
+ let aliased_type = self.aliased_types.entry(target_did).or_insert_with(|| {
+ let impl_ = cache
+ .impls
+ .get(&target_did)
+ .map(|v| &v[..])
+ .unwrap_or_default()
+ .iter()
+ .map(|impl_| {
+ (
+ impl_.impl_item.item_id,
+ AliasedTypeImpl { impl_, type_aliases: Vec::new() },
+ )
+ })
+ .collect();
+ AliasedType { target_fqp: &target_fqp[..], target_type, impl_ }
+ });
+ let get_local = { || cache.paths.get(&self_did).map(|(p, _)| p) };
+ let Some(self_fqp) = cache.exact_paths.get(&self_did).or_else(get_local) else {
+ return;
+ };
+ let aliased_ty = self.cx.tcx().type_of(self_did).skip_binder();
+ // Exclude impls that are directly on this type. They're already in the HTML.
+ // Some inlining scenarios can cause there to be two versions of the same
+ // impl: one on the type alias and one on the underlying target type.
+ let mut seen_impls: FxHashSet<ItemId> = cache
+ .impls
+ .get(&self_did)
+ .map(|s| &s[..])
+ .unwrap_or_default()
+ .iter()
+ .map(|i| i.impl_item.item_id)
+ .collect();
+ for (impl_item_id, aliased_type_impl) in &mut aliased_type.impl_ {
+ // Only include this impl if it actually unifies with this alias.
+ // Synthetic impls are not included; those are also included in the HTML.
+ //
+ // FIXME(lazy_type_alias): Once the feature is complete or stable, rewrite this
+ // to use type unification.
+ // Be aware of `tests/rustdoc/type-alias/deeply-nested-112515.rs` which might regress.
+ let Some(impl_did) = impl_item_id.as_def_id() else { continue };
+ let for_ty = self.cx.tcx().type_of(impl_did).skip_binder();
+ let reject_cx =
+ DeepRejectCtxt { treat_obligation_params: TreatParams::AsCandidateKey };
+ if !reject_cx.types_may_unify(aliased_ty, for_ty) {
+ continue;
+ }
+ // Avoid duplicates
+ if !seen_impls.insert(*impl_item_id) {
+ continue;
+ }
+ // This impl was not found in the set of rejected impls
+ aliased_type_impl.type_aliases.push((&self_fqp[..], it.clone()));
+ }
+ }
+ }
+ let mut type_impl_collector = TypeImplCollector {
+ aliased_types: IndexMap::default(),
+ visited_aliases: FxHashSet::default(),
+ cache,
+ cx,
+ };
+ DocVisitor::visit_crate(&mut type_impl_collector, &krate);
+ // Final serialized form of the alias impl
+ struct AliasSerializableImpl {
+ text: String,
+ trait_: Option<String>,
+ aliases: Vec<String>,
+ }
+ impl Serialize for AliasSerializableImpl {
+ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+ where
+ S: Serializer,
+ {
+ let mut seq = serializer.serialize_seq(None)?;
+ seq.serialize_element(&self.text)?;
+ if let Some(trait_) = &self.trait_ {
+ seq.serialize_element(trait_)?;
+ } else {
+ seq.serialize_element(&0)?;
+ }
+ for type_ in &self.aliases {
+ seq.serialize_element(type_)?;
+ }
+ seq.end()
+ }
+ }
+ let cx = type_impl_collector.cx;
+ let dst = cx.dst.join("type.impl");
+ let aliased_types = type_impl_collector.aliased_types;
+ for aliased_type in aliased_types.values() {
+ let impls = aliased_type
+ .impl_
+ .values()
+ .flat_map(|AliasedTypeImpl { impl_, type_aliases }| {
+ let mut ret = Vec::new();
+ let trait_ = impl_
+ .inner_impl()
+ .trait_
+ .as_ref()
+ .map(|trait_| format!("{:#}", trait_.print(cx)));
+ // render_impl will filter out "impossible-to-call" methods
+ // to make that functionality work here, it needs to be called with
+ // each type alias, and if it gives a different result, split the impl
+ for &(type_alias_fqp, ref type_alias_item) in type_aliases {
+ let mut buf = Buffer::html();
+ cx.id_map = Default::default();
+ cx.deref_id_map = Default::default();
+ let target_did = impl_
+ .inner_impl()
+ .trait_
+ .as_ref()
+ .map(|trait_| trait_.def_id())
+ .or_else(|| impl_.inner_impl().for_.def_id(cache));
+ let provided_methods;
+ let assoc_link = if let Some(target_did) = target_did {
+ provided_methods = impl_.inner_impl().provided_trait_methods(cx.tcx());
+ AssocItemLink::GotoSource(ItemId::DefId(target_did), &provided_methods)
+ } else {
+ AssocItemLink::Anchor(None)
+ };
+ super::render_impl(
+ &mut buf,
+ cx,
+ *impl_,
+ &type_alias_item,
+ assoc_link,
+ RenderMode::Normal,
+ None,
+ &[],
+ ImplRenderingParameters {
+ show_def_docs: true,
+ show_default_items: true,
+ show_non_assoc_items: true,
+ toggle_open_by_default: true,
+ },
+ );
+ let text = buf.into_inner();
+ let type_alias_fqp = (*type_alias_fqp).iter().join("::");
+ if Some(&text) == ret.last().map(|s: &AliasSerializableImpl| &s.text) {
+ ret.last_mut()
+ .expect("already established that ret.last() is Some()")
+ .aliases
+ .push(type_alias_fqp);
+ } else {
+ ret.push(AliasSerializableImpl {
+ text,
+ trait_: trait_.clone(),
+ aliases: vec![type_alias_fqp],
+ })
+ }
+ }
+ ret
+ })
+ .collect::<Vec<_>>();
+ let impls = format!(
+ r#""{}":{}"#,
+ krate.name(cx.tcx()),
+ serde_json::to_string(&impls).expect("failed serde conversion"),
+ );
+
+ let mut mydst = dst.clone();
+ for part in &aliased_type.target_fqp[..aliased_type.target_fqp.len() - 1] {
+ mydst.push(part.to_string());
+ }
+ cx.shared.ensure_dir(&mydst)?;
+ let aliased_item_type = aliased_type.target_type;
+ mydst.push(&format!(
+ "{aliased_item_type}.{}.js",
+ aliased_type.target_fqp[aliased_type.target_fqp.len() - 1]
+ ));
+
+ let (mut all_impls, _) = try_err!(collect(&mydst, krate.name(cx.tcx()).as_str()), &mydst);
+ all_impls.push(impls);
+ // Sort the implementors by crate so the file will be generated
+ // identically even with rustdoc running in parallel.
+ all_impls.sort();
+
+ let mut v = String::from("(function() {var type_impls = {\n");
+ v.push_str(&all_impls.join(",\n"));
+ v.push_str("\n};");
+ v.push_str(
+ "if (window.register_type_impls) {\
+ window.register_type_impls(type_impls);\
+ } else {\
+ window.pending_type_impls = type_impls;\
+ }",
+ );
+ v.push_str("})()");
+ cx.shared.fs.write(mydst, v)?;
+ }
+
// Update the list of all implementors for traits
- let dst = cx.dst.join("implementors");
- let cache = cx.cache();
+ // <https://github.com/search?q=repo%3Arust-lang%2Frust+[RUSTDOCIMPL]+trait.impl&type=code>
+ let dst = cx.dst.join("trait.impl");
for (&did, imps) in &cache.implementors {
// Private modules can leak through to this phase of rustdoc, which
// could contain implementations for otherwise private types. In some
diff --git a/src/librustdoc/html/sources.rs b/src/librustdoc/html/sources.rs
index 1d6eafe51..ce620c226 100644
--- a/src/librustdoc/html/sources.rs
+++ b/src/librustdoc/html/sources.rs
@@ -1,4 +1,5 @@
use crate::clean;
+use crate::clean::utils::has_doc_flag;
use crate::docfs::PathError;
use crate::error::Error;
use crate::html::format;
@@ -12,7 +13,7 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_hir::def_id::LOCAL_CRATE;
use rustc_middle::ty::TyCtxt;
use rustc_session::Session;
-use rustc_span::source_map::FileName;
+use rustc_span::{sym, FileName};
use std::cell::RefCell;
use std::ffi::OsStr;
@@ -223,7 +224,8 @@ impl SourceCollector<'_, '_> {
cur.push(&fname);
let title = format!("{} - source", src_fname.to_string_lossy());
- let desc = format!("Source of the Rust file `{}`.", filename.prefer_remapped());
+ let desc =
+ format!("Source of the Rust file `{}`.", filename.prefer_remapped_unconditionaly());
let page = layout::Page {
title: &title,
css_class: "src",
@@ -231,6 +233,7 @@ impl SourceCollector<'_, '_> {
static_root_path: shared.static_root_path.as_deref(),
description: &desc,
resource_suffix: &shared.resource_suffix,
+ rust_logo: has_doc_flag(self.cx.tcx(), LOCAL_CRATE.as_def_id(), sym::rust_logo),
};
let v = layout::render(
&shared.layout,
diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css
index 47f9e6502..9efdcd601 100644
--- a/src/librustdoc/html/static/css/rustdoc.css
+++ b/src/librustdoc/html/static/css/rustdoc.css
@@ -461,19 +461,9 @@ img {
display: none !important;
}
-.sidebar .logo-container {
- margin-top: 10px;
- margin-bottom: 10px;
- text-align: center;
-}
-
-.version {
- overflow-wrap: break-word;
-}
-
.logo-container > img {
- height: 100px;
- width: 100px;
+ height: 48px;
+ width: 48px;
}
ul.block, .block li {
@@ -502,6 +492,7 @@ ul.block, .block li {
}
.sidebar-elems,
+.sidebar > .version,
.sidebar > h2 {
padding-left: 24px;
}
@@ -510,6 +501,8 @@ ul.block, .block li {
color: var(--sidebar-link-color);
}
.sidebar .current,
+.sidebar .current a,
+.sidebar-crate a.logo-container:hover + h2 a,
.sidebar a:hover:not(.logo-container) {
background-color: var(--sidebar-current-link-background-color);
}
@@ -524,6 +517,75 @@ ul.block, .block li {
overflow: hidden;
}
+.sidebar-crate {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ /* there's a 10px padding at the top of <main>, and a 4px margin at the
+ top of the search form. To line them up, add them. */
+ margin: 14px 32px 1rem;
+ row-gap: 10px;
+ column-gap: 32px;
+ flex-wrap: wrap;
+}
+
+.sidebar-crate h2 {
+ flex-grow: 1;
+ /* This setup with the margins and row-gap is designed to make flex-wrap
+ work the way we want. If they're in the side-by-side lockup, there
+ should be a 16px margin to the left of the logo (visually the same as
+ the 24px one on everything else, which are not giant circles) and 8px
+ between it and the crate's name and version. When they're line wrapped,
+ the logo needs to have the same margin on both sides of itself (to
+ center properly) and the crate name and version need 24px on their
+ left margin. */
+ margin: 0 -8px;
+ /* To align this with the search bar, it should not be centered, even when
+ the logo is. */
+ align-self: start;
+}
+
+.sidebar-crate .logo-container {
+ /* The logo is expected to have 8px "slop" along its edges, so we can optically
+ center it. */
+ margin: 0 -16px 0 -16px;
+ text-align: center;
+}
+
+.sidebar-crate h2 a {
+ display: block;
+ margin: 0 calc(-24px + 0.25rem) 0 -0.5rem;
+ /* Align the sidebar crate link with the search bar, which have different
+ font sizes.
+
+ | | font-size | line-height | total line-height | padding-y | total |
+ |:-------|----------:|------------:|------------------:|----------:|-------------:|
+ | crate | 1.375rem | 1.25 | 1.72rem | x | 2x+1.72rem |
+ | search | 1rem | 1.15 | 1.15rem | 8px | 1.15rem+16px |
+
+ 2x + 1.72rem = 1.15rem + 16px
+ 2x = 1.15rem + 16px - 1.72rem
+ 2x = 16px - 0.57rem
+ x = ( 16px - 0.57rem ) / 2
+ */
+ padding: calc( ( 16px - 0.57rem ) / 2 ) 0.25rem;
+ padding-left: 0.5rem;
+}
+
+.sidebar-crate h2 .version {
+ display: block;
+ font-weight: normal;
+ font-size: 1rem;
+ overflow-wrap: break-word;
+ /* opposite of the link padding, cut in half again */
+ margin-top: calc( ( -16px + 0.57rem ) / 2 );
+}
+
+.sidebar-crate + .version {
+ margin-top: -1rem;
+ margin-bottom: 1rem;
+}
+
.mobile-topbar {
display: none;
}
@@ -1045,6 +1107,7 @@ so that we can apply CSS-filters to change the arrow color in themes */
white-space: pre-wrap;
border-radius: 3px;
display: inline;
+ vertical-align: baseline;
}
.stab.portability > code {
diff --git a/src/librustdoc/html/static/js/main.js b/src/librustdoc/html/static/js/main.js
index eb256455b..7c052606a 100644
--- a/src/librustdoc/html/static/js/main.js
+++ b/src/librustdoc/html/static/js/main.js
@@ -51,9 +51,14 @@ function setMobileTopbar() {
// but with the current code it's hard to get the right information in the right place.
const mobileTopbar = document.querySelector(".mobile-topbar");
const locationTitle = document.querySelector(".sidebar h2.location");
- if (mobileTopbar && locationTitle) {
+ if (mobileTopbar) {
const mobileTitle = document.createElement("h2");
- mobileTitle.innerHTML = locationTitle.innerHTML;
+ mobileTitle.className = "location";
+ if (hasClass(document.body, "crate")) {
+ mobileTitle.innerText = `Crate ${window.currentCrate}`;
+ } else if (locationTitle) {
+ mobileTitle.innerHTML = locationTitle.innerHTML;
+ }
mobileTopbar.appendChild(mobileTitle);
}
}
@@ -354,6 +359,34 @@ function preLoadCss(cssUrl) {
expandSection(pageId);
}
}
+ if (savedHash.startsWith("impl-")) {
+ // impl-disambiguated links, used by the search engine
+ // format: impl-X[-for-Y]/method.WHATEVER
+ // turn this into method.WHATEVER[-NUMBER]
+ const splitAt = savedHash.indexOf("/");
+ if (splitAt !== -1) {
+ const implId = savedHash.slice(0, splitAt);
+ const assocId = savedHash.slice(splitAt + 1);
+ const implElem = document.getElementById(implId);
+ if (implElem && implElem.parentElement.tagName === "SUMMARY" &&
+ implElem.parentElement.parentElement.tagName === "DETAILS") {
+ onEachLazy(implElem.parentElement.parentElement.querySelectorAll(
+ `[id^="${assocId}"]`),
+ item => {
+ const numbered = /([^-]+)-([0-9]+)/.exec(item.id);
+ if (item.id === assocId || (numbered && numbered[1] === assocId)) {
+ openParentDetails(item);
+ item.scrollIntoView();
+ // Let the section expand itself before trying to highlight
+ setTimeout(() => {
+ window.location.replace("#" + item.id);
+ }, 0);
+ }
+ }
+ );
+ }
+ }
+ }
}
function onHashChange(ev) {
@@ -452,22 +485,27 @@ function preLoadCss(cssUrl) {
return;
}
+ const modpath = hasClass(document.body, "mod") ? "../" : "";
+
const h3 = document.createElement("h3");
- h3.innerHTML = `<a href="index.html#${id}">${longty}</a>`;
+ h3.innerHTML = `<a href="${modpath}index.html#${id}">${longty}</a>`;
const ul = document.createElement("ul");
ul.className = "block " + shortty;
for (const name of filtered) {
let path;
if (shortty === "mod") {
- path = name + "/index.html";
+ path = `${modpath}${name}/index.html`;
} else {
- path = shortty + "." + name + ".html";
+ path = `${modpath}${shortty}.${name}.html`;
+ }
+ let current_page = document.location.href.toString();
+ if (current_page.endsWith("/")) {
+ current_page += "index.html";
}
- const current_page = document.location.href.split("/").pop();
const link = document.createElement("a");
link.href = path;
- if (path === current_page) {
+ if (link.href === current_page) {
link.className = "current";
}
link.textContent = name;
@@ -480,23 +518,38 @@ function preLoadCss(cssUrl) {
}
if (sidebar) {
+ // keep this synchronized with ItemSection::ALL in html/render/mod.rs
+ // Re-exports aren't shown here, because they don't have child pages
+ //block("reexport", "reexports", "Re-exports");
block("primitive", "primitives", "Primitive Types");
block("mod", "modules", "Modules");
block("macro", "macros", "Macros");
block("struct", "structs", "Structs");
block("enum", "enums", "Enums");
- block("union", "unions", "Unions");
block("constant", "constants", "Constants");
block("static", "static", "Statics");
block("trait", "traits", "Traits");
block("fn", "functions", "Functions");
block("type", "types", "Type Aliases");
+ block("union", "unions", "Unions");
+ // No point, because these items don't appear in modules
+ //block("impl", "impls", "Implementations");
+ //block("tymethod", "tymethods", "Type Methods");
+ //block("method", "methods", "Methods");
+ //block("structfield", "fields", "Fields");
+ //block("variant", "variants", "Variants");
+ //block("associatedtype", "associated-types", "Associated Types");
+ //block("associatedconstant", "associated-consts", "Associated Constants");
block("foreigntype", "foreign-types", "Foreign Types");
block("keyword", "keywords", "Keywords");
+ block("opaque", "opaque-types", "Opaque Types");
+ block("attr", "attributes", "Attribute Macros");
+ block("derive", "derives", "Derive Macros");
block("traitalias", "trait-aliases", "Trait Aliases");
}
}
+ // <https://github.com/search?q=repo%3Arust-lang%2Frust+[RUSTDOCIMPL]+trait.impl&type=code>
window.register_implementors = imp => {
const implementors = document.getElementById("implementors-list");
const synthetic_implementors = document.getElementById("synthetic-implementors-list");
@@ -563,7 +616,7 @@ function preLoadCss(cssUrl) {
onEachLazy(code.getElementsByTagName("a"), elem => {
const href = elem.getAttribute("href");
- if (href && !/^(?:[a-z+]+:)?\/\//.test(href)) {
+ if (href && !href.startsWith("#") && !/^(?:[a-z+]+:)?\/\//.test(href)) {
elem.setAttribute("href", window.rootPath + href);
}
});
@@ -587,6 +640,216 @@ function preLoadCss(cssUrl) {
window.register_implementors(window.pending_implementors);
}
+ /**
+ * <https://github.com/search?q=repo%3Arust-lang%2Frust+[RUSTDOCIMPL]+type.impl&type=code>
+ *
+ * [RUSTDOCIMPL] type.impl
+ *
+ * This code inlines implementations into the type alias docs at runtime. It's done at
+ * runtime because some crates have many type aliases and many methods, and we don't want
+ * to generate *O*`(types*methods)` HTML text. The data inside is mostly HTML fragments,
+ * wrapped in JSON.
+ *
+ * - It only includes docs generated for the current crate. This function accepts an
+ * object mapping crate names to the set of impls.
+ *
+ * - It filters down to the set of applicable impls. The Rust type checker is used to
+ * tag each HTML blob with the set of type aliases that can actually use it, so the
+ * JS only needs to consult the attached list of type aliases.
+ *
+ * - It renames the ID attributes, to avoid conflicting IDs in the resulting DOM.
+ *
+ * - It adds the necessary items to the sidebar. If it's an inherent impl, that means
+ * adding methods, associated types, and associated constants. If it's a trait impl,
+ * that means adding it to the trait impl sidebar list.
+ *
+ * - It adds the HTML block itself. If it's an inherent impl, it goes after the type
+ * alias's own inherent impls. If it's a trait impl, it goes in the Trait
+ * Implementations section.
+ *
+ * - After processing all of the impls, it sorts the sidebar items by name.
+ *
+ * @param {{[cratename: string]: Array<Array<string|0>>}} impl
+ */
+ window.register_type_impls = imp => {
+ if (!imp || !imp[window.currentCrate]) {
+ return;
+ }
+ window.pending_type_impls = null;
+ const idMap = new Map();
+
+ let implementations = document.getElementById("implementations-list");
+ let trait_implementations = document.getElementById("trait-implementations-list");
+ let trait_implementations_header = document.getElementById("trait-implementations");
+
+ // We want to include the current type alias's impls, and no others.
+ const script = document.querySelector("script[data-self-path]");
+ const selfPath = script ? script.getAttribute("data-self-path") : null;
+
+ // These sidebar blocks need filled in, too.
+ const mainContent = document.querySelector("#main-content");
+ const sidebarSection = document.querySelector(".sidebar section");
+ let methods = document.querySelector(".sidebar .block.method");
+ let associatedTypes = document.querySelector(".sidebar .block.associatedtype");
+ let associatedConstants = document.querySelector(".sidebar .block.associatedconstant");
+ let sidebarTraitList = document.querySelector(".sidebar .block.trait-implementation");
+
+ for (const impList of imp[window.currentCrate]) {
+ const types = impList.slice(2);
+ const text = impList[0];
+ const isTrait = impList[1] !== 0;
+ const traitName = impList[1];
+ if (types.indexOf(selfPath) === -1) {
+ continue;
+ }
+ let outputList = isTrait ? trait_implementations : implementations;
+ if (outputList === null) {
+ const outputListName = isTrait ? "Trait Implementations" : "Implementations";
+ const outputListId = isTrait ?
+ "trait-implementations-list" :
+ "implementations-list";
+ const outputListHeaderId = isTrait ? "trait-implementations" : "implementations";
+ const outputListHeader = document.createElement("h2");
+ outputListHeader.id = outputListHeaderId;
+ outputListHeader.innerText = outputListName;
+ outputList = document.createElement("div");
+ outputList.id = outputListId;
+ if (isTrait) {
+ const link = document.createElement("a");
+ link.href = `#${outputListHeaderId}`;
+ link.innerText = "Trait Implementations";
+ const h = document.createElement("h3");
+ h.appendChild(link);
+ trait_implementations = outputList;
+ trait_implementations_header = outputListHeader;
+ sidebarSection.appendChild(h);
+ sidebarTraitList = document.createElement("ul");
+ sidebarTraitList.className = "block trait-implementation";
+ sidebarSection.appendChild(sidebarTraitList);
+ mainContent.appendChild(outputListHeader);
+ mainContent.appendChild(outputList);
+ } else {
+ implementations = outputList;
+ if (trait_implementations) {
+ mainContent.insertBefore(outputListHeader, trait_implementations_header);
+ mainContent.insertBefore(outputList, trait_implementations_header);
+ } else {
+ const mainContent = document.querySelector("#main-content");
+ mainContent.appendChild(outputListHeader);
+ mainContent.appendChild(outputList);
+ }
+ }
+ }
+ const template = document.createElement("template");
+ template.innerHTML = text;
+
+ onEachLazy(template.content.querySelectorAll("a"), elem => {
+ const href = elem.getAttribute("href");
+
+ if (href && !href.startsWith("#") && !/^(?:[a-z+]+:)?\/\//.test(href)) {
+ elem.setAttribute("href", window.rootPath + href);
+ }
+ });
+ onEachLazy(template.content.querySelectorAll("[id]"), el => {
+ let i = 0;
+ if (idMap.has(el.id)) {
+ i = idMap.get(el.id);
+ } else if (document.getElementById(el.id)) {
+ i = 1;
+ while (document.getElementById(`${el.id}-${2 * i}`)) {
+ i = 2 * i;
+ }
+ while (document.getElementById(`${el.id}-${i}`)) {
+ i += 1;
+ }
+ }
+ if (i !== 0) {
+ const oldHref = `#${el.id}`;
+ const newHref = `#${el.id}-${i}`;
+ el.id = `${el.id}-${i}`;
+ onEachLazy(template.content.querySelectorAll("a[href]"), link => {
+ if (link.getAttribute("href") === oldHref) {
+ link.href = newHref;
+ }
+ });
+ }
+ idMap.set(el.id, i + 1);
+ });
+ const templateAssocItems = template.content.querySelectorAll("section.tymethod, " +
+ "section.method, section.associatedtype, section.associatedconstant");
+ if (isTrait) {
+ const li = document.createElement("li");
+ const a = document.createElement("a");
+ a.href = `#${template.content.querySelector(".impl").id}`;
+ a.textContent = traitName;
+ li.appendChild(a);
+ sidebarTraitList.append(li);
+ } else {
+ onEachLazy(templateAssocItems, item => {
+ let block = hasClass(item, "associatedtype") ? associatedTypes : (
+ hasClass(item, "associatedconstant") ? associatedConstants : (
+ methods));
+ if (!block) {
+ const blockTitle = hasClass(item, "associatedtype") ? "Associated Types" : (
+ hasClass(item, "associatedconstant") ? "Associated Constants" : (
+ "Methods"));
+ const blockClass = hasClass(item, "associatedtype") ? "associatedtype" : (
+ hasClass(item, "associatedconstant") ? "associatedconstant" : (
+ "method"));
+ const blockHeader = document.createElement("h3");
+ const blockLink = document.createElement("a");
+ blockLink.href = "#implementations";
+ blockLink.innerText = blockTitle;
+ blockHeader.appendChild(blockLink);
+ block = document.createElement("ul");
+ block.className = `block ${blockClass}`;
+ const insertionReference = methods || sidebarTraitList;
+ if (insertionReference) {
+ const insertionReferenceH = insertionReference.previousElementSibling;
+ sidebarSection.insertBefore(blockHeader, insertionReferenceH);
+ sidebarSection.insertBefore(block, insertionReferenceH);
+ } else {
+ sidebarSection.appendChild(blockHeader);
+ sidebarSection.appendChild(block);
+ }
+ if (hasClass(item, "associatedtype")) {
+ associatedTypes = block;
+ } else if (hasClass(item, "associatedconstant")) {
+ associatedConstants = block;
+ } else {
+ methods = block;
+ }
+ }
+ const li = document.createElement("li");
+ const a = document.createElement("a");
+ a.innerText = item.id.split("-")[0].split(".")[1];
+ a.href = `#${item.id}`;
+ li.appendChild(a);
+ block.appendChild(li);
+ });
+ }
+ outputList.appendChild(template.content);
+ }
+
+ for (const list of [methods, associatedTypes, associatedConstants, sidebarTraitList]) {
+ if (!list) {
+ continue;
+ }
+ const newChildren = Array.prototype.slice.call(list.children);
+ newChildren.sort((a, b) => {
+ const aI = a.innerText;
+ const bI = b.innerText;
+ return aI < bI ? -1 :
+ aI > bI ? 1 :
+ 0;
+ });
+ list.replaceChildren(...newChildren);
+ }
+ };
+ if (window.pending_type_impls) {
+ window.register_type_impls(window.pending_type_impls);
+ }
+
function addSidebarCrates() {
if (!window.ALL_CRATES) {
return;
diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js
index 2f0cae0a4..48c9a53a2 100644
--- a/src/librustdoc/html/static/js/search.js
+++ b/src/librustdoc/html/static/js/search.js
@@ -1555,7 +1555,7 @@ function initSearch(rawSearchIndex) {
return false;
}
}
- } else if (fnType.id !== null) {
+ } else {
if (queryElem.id === typeNameIdOfArrayOrSlice &&
(fnType.id === typeNameIdOfSlice || fnType.id === typeNameIdOfArray)
) {
@@ -1752,6 +1752,7 @@ function initSearch(rawSearchIndex) {
type: item.type,
is_alias: true,
deprecated: item.deprecated,
+ implDisambiguator: item.implDisambiguator,
};
}
@@ -2218,7 +2219,7 @@ function initSearch(rawSearchIndex) {
href = ROOT_PATH + name + "/index.html";
} else if (item.parent !== undefined) {
const myparent = item.parent;
- let anchor = "#" + type + "." + name;
+ let anchor = type + "." + name;
const parentType = itemTypes[myparent.ty];
let pageType = parentType;
let pageName = myparent.name;
@@ -2232,16 +2233,19 @@ function initSearch(rawSearchIndex) {
const enumName = item.path.substr(enumNameIdx + 2);
path = item.path.substr(0, enumNameIdx);
displayPath = path + "::" + enumName + "::" + myparent.name + "::";
- anchor = "#variant." + myparent.name + ".field." + name;
+ anchor = "variant." + myparent.name + ".field." + name;
pageType = "enum";
pageName = enumName;
} else {
displayPath = path + "::" + myparent.name + "::";
}
+ if (item.implDisambiguator !== null) {
+ anchor = item.implDisambiguator + "/" + anchor;
+ }
href = ROOT_PATH + path.replace(/::/g, "/") +
"/" + pageType +
"." + pageName +
- ".html" + anchor;
+ ".html#" + anchor;
} else {
displayPath = item.path + "::";
href = ROOT_PATH + item.path.replace(/::/g, "/") +
@@ -2727,6 +2731,10 @@ ${item.displayPath}<span class="${type}">${name}</span>\
* Types are also represented as arrays; the first item is an index into the `p`
* array, while the second is a list of types representing any generic parameters.
*
+ * b[i] contains an item's impl disambiguator. This is only present if an item
+ * is defined in an impl block and, the impl block's type has more than one associated
+ * item with the same name.
+ *
* `a` defines aliases with an Array of pairs: [name, offset], where `offset`
* points into the n/t/d/q/i/f arrays.
*
@@ -2746,6 +2754,7 @@ ${item.displayPath}<span class="${type}">${name}</span>\
* i: Array<Number>,
* f: Array<RawFunctionSearchType>,
* p: Array<Object>,
+ * b: Array<[Number, String]>,
* c: Array<Number>
* }}
*/
@@ -2766,6 +2775,7 @@ ${item.displayPath}<span class="${type}">${name}</span>\
id: id,
normalizedName: crate.indexOf("_") === -1 ? crate : crate.replace(/_/g, ""),
deprecated: null,
+ implDisambiguator: null,
};
id += 1;
searchIndex.push(crateRow);
@@ -2789,6 +2799,8 @@ ${item.displayPath}<span class="${type}">${name}</span>\
const itemFunctionSearchTypes = crateCorpus.f;
// an array of (Number) indices for the deprecated items
const deprecatedItems = new Set(crateCorpus.c);
+ // an array of (Number) indices for the deprecated items
+ const implDisambiguator = new Map(crateCorpus.b);
// an array of [(Number) item type,
// (String) name]
const paths = crateCorpus.p;
@@ -2849,6 +2861,7 @@ ${item.displayPath}<span class="${type}">${name}</span>\
id: id,
normalizedName: word.indexOf("_") === -1 ? word : word.replace(/_/g, ""),
deprecated: deprecatedItems.has(i),
+ implDisambiguator: implDisambiguator.has(i) ? implDisambiguator.get(i) : null,
};
id += 1;
searchIndex.push(row);
diff --git a/src/librustdoc/html/templates/page.html b/src/librustdoc/html/templates/page.html
index 579c782be..3f6147bb9 100644
--- a/src/librustdoc/html/templates/page.html
+++ b/src/librustdoc/html/templates/page.html
@@ -10,7 +10,6 @@
<link rel="preload" as="font" type="font/woff2" crossorigin href="{{static_root_path|safe}}{{files.fira_sans_regular}}"> {# #}
<link rel="preload" as="font" type="font/woff2" crossorigin href="{{static_root_path|safe}}{{files.fira_sans_medium}}"> {# #}
<link rel="preload" as="font" type="font/woff2" crossorigin href="{{static_root_path|safe}}{{files.source_code_pro_regular}}"> {# #}
- <link rel="preload" as="font" type="font/woff2" crossorigin href="{{static_root_path|safe}}{{files.source_serif_4_bold}}"> {# #}
<link rel="preload" as="font" type="font/woff2" crossorigin href="{{static_root_path|safe}}{{files.source_code_pro_semibold}}"> {# #}
<link rel="stylesheet" {#+ #}
href="{{static_root_path|safe}}{{files.normalize_css}}"> {# #}
@@ -42,6 +41,8 @@
<script defer src="{{page.root_path|safe}}src-files{{page.resource_suffix}}.js"></script> {# #}
{% else if !page.css_class.contains("mod") %}
<script defer src="sidebar-items{{page.resource_suffix}}.js"></script> {# #}
+ {% else if !page.css_class.contains("sys") %}
+ <script defer src="../sidebar-items{{page.resource_suffix}}.js"></script> {# #}
{% endif %}
<script defer src="{{static_root_path|safe}}{{files.main_js}}"></script> {# #}
{% if layout.scrape_examples_extension %}
@@ -77,36 +78,51 @@
{% if page.css_class != "src" %}
<nav class="mobile-topbar"> {# #}
<button class="sidebar-menu-toggle">&#9776;</button> {# #}
- <a class="logo-container" href="{{page.root_path|safe}}{{krate_with_trailing_slash|safe}}index.html"> {# #}
- {% if !layout.logo.is_empty() %}
- <img src="{{layout.logo}}" alt="logo"> {# #}
- {% else %}
- <img class="rust-logo" src="{{static_root_path|safe}}{{files.rust_logo_svg}}" alt="logo"> {# #}
+ {% if !layout.logo.is_empty() || page.rust_logo %}
+ <a class="logo-container" href="{{page.root_path|safe}}{{display_krate_with_trailing_slash|safe}}index.html"> {# #}
+ {% if page.rust_logo %}
+ <img class="rust-logo" src="{{static_root_path|safe}}{{files.rust_logo_svg}}" alt=""> {# #}
+ {% else if !layout.logo.is_empty() %}
+ <img src="{{layout.logo}}" alt=""> {# #}
{% endif %}
</a> {# #}
+ {% endif %}
</nav>
{% endif %}
<nav class="sidebar"> {# #}
{% if page.css_class != "src" %}
- <a class="logo-container" href="{{page.root_path|safe}}{{krate_with_trailing_slash|safe}}index.html"> {# #}
- {% if !layout.logo.is_empty() %}
- <img src="{{layout.logo}}" alt="logo"> {# #}
- {% else %}
- <img class="rust-logo" src="{{static_root_path|safe}}{{files.rust_logo_svg}}" alt="logo"> {# #}
+ <div class="sidebar-crate">
+ {% if !layout.logo.is_empty() || page.rust_logo %}
+ <a class="logo-container" href="{{page.root_path|safe}}{{display_krate_with_trailing_slash|safe}}index.html"> {# #}
+ {% if page.rust_logo %}
+ <img class="rust-logo" src="{{static_root_path|safe}}{{files.rust_logo_svg}}" alt="logo"> {# #}
+ {% else if !layout.logo.is_empty() %}
+ <img src="{{layout.logo}}" alt="logo"> {# #}
+ {% endif %}
+ </a> {# #}
{% endif %}
- </a> {# #}
+ <h2> {# #}
+ <a href="{{page.root_path|safe}}{{display_krate_with_trailing_slash|safe}}index.html">{{display_krate}}</a> {# #}
+ {% if !display_krate_version_number.is_empty() %}
+ <span class="version">{{+ display_krate_version_number}}</span>
+ {% endif %}
+ </h2> {# #}
+ </div> {# #}
+ {% if !display_krate_version_extra.is_empty() %}
+ <div class="version">{{+ display_krate_version_extra}}</div> {# #}
+ {% endif %}
{% endif %}
{{ sidebar|safe }}
</nav> {# #}
<main> {# #}
{% if page.css_class != "src" %}<div class="width-limiter">{% endif %}
<nav class="sub"> {# #}
- {% if page.css_class == "src" %}
- <a class="sub-logo-container" href="{{page.root_path|safe}}{{krate_with_trailing_slash|safe}}index.html"> {# #}
- {% if !layout.logo.is_empty() %}
- <img src="{{layout.logo}}" alt="logo"> {# #}
- {% else %}
- <img class="rust-logo" src="{{static_root_path|safe}}{{files.rust_logo_svg}}" alt="logo"> {# #}
+ {% if page.css_class == "src" && (!layout.logo.is_empty() || page.rust_logo) %}
+ <a class="sub-logo-container" href="{{page.root_path|safe}}{{display_krate_with_trailing_slash|safe}}index.html"> {# #}
+ {% if page.rust_logo %}
+ <img class="rust-logo" src="{{static_root_path|safe}}{{files.rust_logo_svg}}" alt="{{display_krate}}"> {# #}
+ {% else if !layout.logo.is_empty() %}
+ <img src="{{layout.logo}}" alt="{{display_krate}}"> {# #}
{% endif %}
</a> {# #}
{% endif %}
diff --git a/src/librustdoc/html/templates/sidebar.html b/src/librustdoc/html/templates/sidebar.html
index 01d476ad2..d98213418 100644
--- a/src/librustdoc/html/templates/sidebar.html
+++ b/src/librustdoc/html/templates/sidebar.html
@@ -6,9 +6,6 @@
<div class="sidebar-elems">
{% if is_crate %}
<ul class="block">
- {% if !version.is_empty() %}
- <li class="version">Version {{+ version}}</li>
- {% endif %}
<li><a id="all-types" href="all.html">All Items</a></li> {# #}
</ul>
{% endif %}
@@ -21,7 +18,7 @@
<h3><a href="#{{block.heading.href|safe}}">{{block.heading.name}}</a></h3>
{% endif %}
{% if !block.links.is_empty() %}
- <ul class="block">
+ <ul class="block{% if !block.class.is_empty() +%} {{+block.class}}{% endif %}">
{% for link in block.links %}
<li><a href="#{{link.href|safe}}">{{link.name}}</a></li>
{% endfor %}
@@ -32,6 +29,6 @@
</section>
{% endif %}
{% if !path.is_empty() %}
- <h2><a href="index.html">In {{+ path}}</a></h2>
+ <h2><a href="{% if is_mod %}../{% endif %}index.html">In {{+ path}}</a></h2>
{% endif %}
</div>
diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs
index 05f3e66b0..285923251 100644
--- a/src/librustdoc/json/conversions.rs
+++ b/src/librustdoc/json/conversions.rs
@@ -7,6 +7,7 @@
use std::fmt;
use rustc_ast::ast;
+use rustc_attr::DeprecatedSince;
use rustc_hir::{def::CtorKind, def::DefKind, def_id::DefId};
use rustc_metadata::rendered_const;
use rustc_middle::ty::{self, TyCtxt};
@@ -18,6 +19,7 @@ use rustdoc_json_types::*;
use crate::clean::{self, ItemId};
use crate::formats::item_type::ItemType;
+use crate::formats::FormatRenderer;
use crate::json::JsonRenderer;
use crate::passes::collect_intra_doc_links::UrlFragment;
@@ -41,7 +43,7 @@ impl JsonRenderer<'_> {
})
.collect();
let docs = item.opt_doc_value();
- let attrs = item.attributes(self.tcx, true);
+ let attrs = item.attributes(self.tcx, self.cache(), true);
let span = item.span(self.tcx);
let visibility = item.visibility(self.tcx);
let clean::Item { name, item_id, .. } = item;
@@ -137,9 +139,14 @@ where
}
pub(crate) fn from_deprecation(deprecation: rustc_attr::Deprecation) -> Deprecation {
- #[rustfmt::skip]
- let rustc_attr::Deprecation { since, note, is_since_rustc_version: _, suggestion: _ } = deprecation;
- Deprecation { since: since.map(|s| s.to_string()), note: note.map(|s| s.to_string()) }
+ let rustc_attr::Deprecation { since, note, suggestion: _ } = deprecation;
+ let since = match since {
+ DeprecatedSince::RustcVersion(version) => Some(version.to_string()),
+ DeprecatedSince::Future => Some("TBD".to_owned()),
+ DeprecatedSince::NonStandard(since) => Some(since.to_string()),
+ DeprecatedSince::Unspecified | DeprecatedSince::Err => None,
+ };
+ Deprecation { since, note: note.map(|s| s.to_string()) }
}
impl FromWithTcx<clean::GenericArgs> for GenericArgs {
@@ -176,7 +183,7 @@ impl FromWithTcx<clean::Constant> for Constant {
let expr = constant.expr(tcx);
let value = constant.value(tcx);
let is_literal = constant.is_literal(tcx);
- Constant { type_: constant.type_.into_tcx(tcx), expr, value, is_literal }
+ Constant { type_: (*constant.type_).into_tcx(tcx), expr, value, is_literal }
}
}
@@ -324,11 +331,11 @@ fn from_clean_item(item: clean::Item, tcx: TyCtxt<'_>) -> ItemEnum {
}
// FIXME(generic_const_items): Add support for generic associated consts.
TyAssocConstItem(_generics, ty) => {
- ItemEnum::AssocConst { type_: ty.into_tcx(tcx), default: None }
+ ItemEnum::AssocConst { type_: (*ty).into_tcx(tcx), default: None }
}
// FIXME(generic_const_items): Add support for generic associated consts.
AssocConstItem(_generics, ty, default) => {
- ItemEnum::AssocConst { type_: ty.into_tcx(tcx), default: Some(default.expr(tcx)) }
+ ItemEnum::AssocConst { type_: (*ty).into_tcx(tcx), default: Some(default.expr(tcx)) }
}
TyAssocTypeItem(g, b) => ItemEnum::AssocType {
generics: g.into_tcx(tcx),
@@ -508,9 +515,8 @@ impl FromWithTcx<clean::WherePredicate> for WherePredicate {
lifetime: convert_lifetime(lifetime),
bounds: bounds.into_tcx(tcx),
},
- // FIXME(fmease): Convert bound parameters as well.
- EqPredicate { lhs, rhs, bound_params: _ } => {
- WherePredicate::EqPredicate { lhs: (*lhs).into_tcx(tcx), rhs: (*rhs).into_tcx(tcx) }
+ EqPredicate { lhs, rhs } => {
+ WherePredicate::EqPredicate { lhs: lhs.into_tcx(tcx), rhs: rhs.into_tcx(tcx) }
}
}
}
@@ -747,7 +753,7 @@ impl FromWithTcx<clean::Discriminant> for Discriminant {
// `rustc_middle` types, not `rustc_hir`, but because JSON never inlines
// the expr is always some.
expr: disr.expr(tcx).unwrap(),
- value: disr.value(tcx),
+ value: disr.value(tcx, false),
}
}
}
diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs
index 67f5ea5d9..dda06d4c9 100644
--- a/src/librustdoc/lib.rs
+++ b/src/librustdoc/lib.rs
@@ -74,6 +74,7 @@ extern crate jemalloc_sys;
use std::env::{self, VarError};
use std::io::{self, IsTerminal};
use std::process;
+use std::sync::{atomic::AtomicBool, Arc};
use rustc_driver::abort_on_err;
use rustc_errors::ErrorGuaranteed;
@@ -157,7 +158,7 @@ pub fn main() {
let mut handler = EarlyErrorHandler::new(ErrorOutputType::default());
- rustc_driver::install_ice_hook(
+ let using_internal_features = rustc_driver::install_ice_hook(
"https://github.com/rust-lang/rust/issues/new\
?labels=C-bug%2C+I-ICE%2C+T-rustdoc&template=ice.md",
|_| (),
@@ -177,7 +178,7 @@ pub fn main() {
rustc_driver::init_env_logger(&handler, "RUSTDOC_LOG");
let exit_code = rustc_driver::catch_with_exit_code(|| match get_args(&handler) {
- Some(args) => main_args(&mut handler, &args),
+ Some(args) => main_args(&mut handler, &args, using_internal_features),
_ =>
{
#[allow(deprecated)]
@@ -701,7 +702,11 @@ fn run_renderer<'tcx, T: formats::FormatRenderer<'tcx>>(
}
}
-fn main_args(handler: &mut EarlyErrorHandler, at_args: &[String]) -> MainResult {
+fn main_args(
+ handler: &mut EarlyErrorHandler,
+ at_args: &[String],
+ using_internal_features: Arc<AtomicBool>,
+) -> MainResult {
// Throw away the first argument, the name of the binary.
// In case of at_args being empty, as might be the case by
// passing empty argument array to execve under some platforms,
@@ -752,7 +757,7 @@ fn main_args(handler: &mut EarlyErrorHandler, at_args: &[String]) -> MainResult
(false, true) => {
let input = options.input.clone();
let edition = options.edition;
- let config = core::create_config(handler, options, &render_options);
+ let config = core::create_config(options, &render_options, using_internal_features);
// `markdown::render` can invoke `doctest::make_test`, which
// requires session globals and a thread pool, so we use
@@ -785,7 +790,7 @@ fn main_args(handler: &mut EarlyErrorHandler, at_args: &[String]) -> MainResult
let scrape_examples_options = options.scrape_examples_options.clone();
let bin_crate = options.bin_crate;
- let config = core::create_config(handler, options, &render_options);
+ let config = core::create_config(options, &render_options, using_internal_features);
interface::run_compiler(config, |compiler| {
let sess = compiler.session();
diff --git a/src/librustdoc/markdown.rs b/src/librustdoc/markdown.rs
index b74a9392f..b661ced01 100644
--- a/src/librustdoc/markdown.rs
+++ b/src/librustdoc/markdown.rs
@@ -4,7 +4,7 @@ use std::io::prelude::*;
use std::path::Path;
use rustc_span::edition::Edition;
-use rustc_span::source_map::DUMMY_SP;
+use rustc_span::DUMMY_SP;
use crate::config::{Options, RenderOptions};
use crate::doctest::{Collector, GlobalTestOptions};
diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs
index d216305e6..fcd078858 100644
--- a/src/librustdoc/passes/collect_intra_doc_links.rs
+++ b/src/librustdoc/passes/collect_intra_doc_links.rs
@@ -521,8 +521,8 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
}
ty::Alias(..)
| ty::Closure(..)
- | ty::Generator(..)
- | ty::GeneratorWitness(..)
+ | ty::Coroutine(..)
+ | ty::CoroutineWitness(..)
| ty::Dynamic(..)
| ty::Param(_)
| ty::Bound(..)
@@ -1918,7 +1918,7 @@ fn resolution_failure(
Variant
| Field
| Closure
- | Generator
+ | Coroutine
| AssocTy
| AssocConst
| AssocFn
diff --git a/src/librustdoc/passes/collect_trait_impls.rs b/src/librustdoc/passes/collect_trait_impls.rs
index ff89d4e08..a57321b58 100644
--- a/src/librustdoc/passes/collect_trait_impls.rs
+++ b/src/librustdoc/passes/collect_trait_impls.rs
@@ -36,7 +36,7 @@ pub(crate) fn collect_trait_impls(mut krate: Crate, cx: &mut DocContext<'_>) ->
let prims: FxHashSet<PrimitiveType> = local_crate.primitives(tcx).iter().map(|p| p.1).collect();
let crate_items = {
- let mut coll = ItemCollector::new();
+ let mut coll = ItemAndAliasCollector::new(&cx.cache);
cx.sess().time("collect_items_for_trait_impls", || coll.visit_crate(&krate));
coll.items
};
@@ -235,21 +235,27 @@ impl<'a, 'tcx> DocVisitor for SyntheticImplCollector<'a, 'tcx> {
}
}
-#[derive(Default)]
-struct ItemCollector {
+struct ItemAndAliasCollector<'cache> {
items: FxHashSet<ItemId>,
+ cache: &'cache Cache,
}
-impl ItemCollector {
- fn new() -> Self {
- Self::default()
+impl<'cache> ItemAndAliasCollector<'cache> {
+ fn new(cache: &'cache Cache) -> Self {
+ ItemAndAliasCollector { items: FxHashSet::default(), cache }
}
}
-impl DocVisitor for ItemCollector {
+impl<'cache> DocVisitor for ItemAndAliasCollector<'cache> {
fn visit_item(&mut self, i: &Item) {
self.items.insert(i.item_id);
+ if let TypeAliasItem(alias) = &*i.kind &&
+ let Some(did) = alias.type_.def_id(self.cache)
+ {
+ self.items.insert(ItemId::DefId(did));
+ }
+
self.visit_item_recur(i)
}
}
diff --git a/src/librustdoc/passes/lint/html_tags.rs b/src/librustdoc/passes/lint/html_tags.rs
index 79fc599e1..00d15a3ca 100644
--- a/src/librustdoc/passes/lint/html_tags.rs
+++ b/src/librustdoc/passes/lint/html_tags.rs
@@ -25,91 +25,85 @@ pub(crate) fn visit_item(cx: &DocContext<'_>, item: &Item) {
Some(sp) => sp,
None => item.attr_span(tcx),
};
- tcx.struct_span_lint_hir(
- crate::lint::INVALID_HTML_TAGS,
- hir_id,
- sp,
- msg.to_string(),
- |lint| {
- use rustc_lint_defs::Applicability;
- // If a tag looks like `<this>`, it might actually be a generic.
- // We don't try to detect stuff `<like, this>` because that's not valid HTML,
- // and we don't try to detect stuff `<like this>` because that's not valid Rust.
- let mut generics_end = range.end;
- if let Some(Some(mut generics_start)) = (is_open_tag
- && dox[..generics_end].ends_with('>'))
- .then(|| extract_path_backwards(&dox, range.start))
+ tcx.struct_span_lint_hir(crate::lint::INVALID_HTML_TAGS, hir_id, sp, msg, |lint| {
+ use rustc_lint_defs::Applicability;
+ // If a tag looks like `<this>`, it might actually be a generic.
+ // We don't try to detect stuff `<like, this>` because that's not valid HTML,
+ // and we don't try to detect stuff `<like this>` because that's not valid Rust.
+ let mut generics_end = range.end;
+ if let Some(Some(mut generics_start)) = (is_open_tag
+ && dox[..generics_end].ends_with('>'))
+ .then(|| extract_path_backwards(&dox, range.start))
+ {
+ while generics_start != 0
+ && generics_end < dox.len()
+ && dox.as_bytes()[generics_start - 1] == b'<'
+ && dox.as_bytes()[generics_end] == b'>'
{
- while generics_start != 0
- && generics_end < dox.len()
- && dox.as_bytes()[generics_start - 1] == b'<'
- && dox.as_bytes()[generics_end] == b'>'
- {
- generics_end += 1;
- generics_start -= 1;
- if let Some(new_start) = extract_path_backwards(&dox, generics_start) {
- generics_start = new_start;
- }
- if let Some(new_end) = extract_path_forward(&dox, generics_end) {
- generics_end = new_end;
- }
+ generics_end += 1;
+ generics_start -= 1;
+ if let Some(new_start) = extract_path_backwards(&dox, generics_start) {
+ generics_start = new_start;
}
if let Some(new_end) = extract_path_forward(&dox, generics_end) {
generics_end = new_end;
}
- let generics_sp = match source_span_for_markdown_range(
- tcx,
- &dox,
- &(generics_start..generics_end),
- &item.attrs.doc_strings,
- ) {
- Some(sp) => sp,
- None => item.attr_span(tcx),
- };
- // Sometimes, we only extract part of a path. For example, consider this:
- //
- // <[u32] as IntoIter<u32>>::Item
- // ^^^^^ unclosed HTML tag `u32`
- //
- // We don't have any code for parsing fully-qualified trait paths.
- // In theory, we could add it, but doing it correctly would require
- // parsing the entire path grammar, which is problematic because of
- // overlap between the path grammar and Markdown.
- //
- // The example above shows that ambiguity. Is `[u32]` intended to be an
- // intra-doc link to the u32 primitive, or is it intended to be a slice?
- //
- // If the below conditional were removed, we would suggest this, which is
- // not what the user probably wants.
- //
- // <[u32] as `IntoIter<u32>`>::Item
- //
- // We know that the user actually wants to wrap the whole thing in a code
- // block, but the only reason we know that is because `u32` does not, in
- // fact, implement IntoIter. If the example looks like this:
- //
- // <[Vec<i32>] as IntoIter<i32>::Item
- //
- // The ideal fix would be significantly different.
- if (generics_start > 0 && dox.as_bytes()[generics_start - 1] == b'<')
- || (generics_end < dox.len() && dox.as_bytes()[generics_end] == b'>')
- {
- return lint;
- }
- // multipart form is chosen here because ``Vec<i32>`` would be confusing.
- lint.multipart_suggestion(
- "try marking as source code",
- vec![
- (generics_sp.shrink_to_lo(), String::from("`")),
- (generics_sp.shrink_to_hi(), String::from("`")),
- ],
- Applicability::MaybeIncorrect,
- );
}
+ if let Some(new_end) = extract_path_forward(&dox, generics_end) {
+ generics_end = new_end;
+ }
+ let generics_sp = match source_span_for_markdown_range(
+ tcx,
+ &dox,
+ &(generics_start..generics_end),
+ &item.attrs.doc_strings,
+ ) {
+ Some(sp) => sp,
+ None => item.attr_span(tcx),
+ };
+ // Sometimes, we only extract part of a path. For example, consider this:
+ //
+ // <[u32] as IntoIter<u32>>::Item
+ // ^^^^^ unclosed HTML tag `u32`
+ //
+ // We don't have any code for parsing fully-qualified trait paths.
+ // In theory, we could add it, but doing it correctly would require
+ // parsing the entire path grammar, which is problematic because of
+ // overlap between the path grammar and Markdown.
+ //
+ // The example above shows that ambiguity. Is `[u32]` intended to be an
+ // intra-doc link to the u32 primitive, or is it intended to be a slice?
+ //
+ // If the below conditional were removed, we would suggest this, which is
+ // not what the user probably wants.
+ //
+ // <[u32] as `IntoIter<u32>`>::Item
+ //
+ // We know that the user actually wants to wrap the whole thing in a code
+ // block, but the only reason we know that is because `u32` does not, in
+ // fact, implement IntoIter. If the example looks like this:
+ //
+ // <[Vec<i32>] as IntoIter<i32>::Item
+ //
+ // The ideal fix would be significantly different.
+ if (generics_start > 0 && dox.as_bytes()[generics_start - 1] == b'<')
+ || (generics_end < dox.len() && dox.as_bytes()[generics_end] == b'>')
+ {
+ return lint;
+ }
+ // multipart form is chosen here because ``Vec<i32>`` would be confusing.
+ lint.multipart_suggestion(
+ "try marking as source code",
+ vec![
+ (generics_sp.shrink_to_lo(), String::from("`")),
+ (generics_sp.shrink_to_hi(), String::from("`")),
+ ],
+ Applicability::MaybeIncorrect,
+ );
+ }
- lint
- },
- );
+ lint
+ });
};
let mut tags = Vec::new();
diff --git a/src/librustdoc/passes/lint/redundant_explicit_links.rs b/src/librustdoc/passes/lint/redundant_explicit_links.rs
index 0c15bf5f7..472781e7d 100644
--- a/src/librustdoc/passes/lint/redundant_explicit_links.rs
+++ b/src/librustdoc/passes/lint/redundant_explicit_links.rs
@@ -87,7 +87,7 @@ fn check_redundant_explicit_link<'md>(
let link_data = collect_link_data(&mut offset_iter);
if let Some(resolvable_link) = link_data.resolvable_link.as_ref() {
- if &link_data.display_link.replace("`", "") != resolvable_link {
+ if &link_data.display_link.replace('`', "") != resolvable_link {
// Skips if display link does not match to actual
// resolvable link, usually happens if display link
// has several segments, e.g.
diff --git a/src/stage0.json b/src/stage0.json
index 8d45d286c..b64dd63ab 100644
--- a/src/stage0.json
+++ b/src/stage0.json
@@ -4,6 +4,7 @@
"artifacts_server": "https://ci-artifacts.rust-lang.org/rustc-builds",
"artifacts_with_llvm_assertions_server": "https://ci-artifacts.rust-lang.org/rustc-builds-alt",
"git_merge_commit_email": "bors@rust-lang.org",
+ "git_repository": "rust-lang/rust",
"nightly_branch": "master"
},
"__comments": [
@@ -17,282 +18,286 @@
"tool is executed."
],
"compiler": {
- "date": "2023-10-05",
- "version": "1.73.0"
+ "date": "2023-11-16",
+ "version": "1.74.0"
},
"rustfmt": null,
"checksums_sha256": {
- "dist/2023-10-05/cargo-1.73.0-aarch64-apple-darwin.tar.gz": "370496a66ccadb3d07800949eee01f0088b3efb220fa80973a7ff7e68b1097c8",
- "dist/2023-10-05/cargo-1.73.0-aarch64-apple-darwin.tar.xz": "caa855d28ade0ecb70567d886048d392b3b90f15a7751f9733d4c189ce67bb71",
- "dist/2023-10-05/cargo-1.73.0-aarch64-pc-windows-msvc.tar.gz": "edb4cf81d70d5e70fe3b92a0369d153e82e0123131fc8176c90942b7b089861f",
- "dist/2023-10-05/cargo-1.73.0-aarch64-pc-windows-msvc.tar.xz": "39b09645c9567ba17881f0af1be6ef4470dc0025254e5a921c1491341b82a860",
- "dist/2023-10-05/cargo-1.73.0-aarch64-unknown-linux-gnu.tar.gz": "f0ef0b9e75613725357f526cd7ac259aac1da37927a8d919eff3eafb8f5087a7",
- "dist/2023-10-05/cargo-1.73.0-aarch64-unknown-linux-gnu.tar.xz": "1195a1d37280802574d729cf00e0dadc63a7c9312a9ae3ef2cf99645f7be0a77",
- "dist/2023-10-05/cargo-1.73.0-aarch64-unknown-linux-musl.tar.gz": "40b9db99e9d5bfbc54c39bbcedc9e900b086fe8d233f722268eb5d719eb1d9a9",
- "dist/2023-10-05/cargo-1.73.0-aarch64-unknown-linux-musl.tar.xz": "aa3115a3cbf4f165436b0c8674d31163434030582971d378f86ff7fc7b1f49c0",
- "dist/2023-10-05/cargo-1.73.0-arm-unknown-linux-gnueabi.tar.gz": "9cfbd484b4ff759060f552d60c08cc3ac547d4369449cc14154474104a3b3b33",
- "dist/2023-10-05/cargo-1.73.0-arm-unknown-linux-gnueabi.tar.xz": "de830c829cff6a01de42ef621f7f1721dfec2036a729597f972b539cfc167798",
- "dist/2023-10-05/cargo-1.73.0-arm-unknown-linux-gnueabihf.tar.gz": "ed5c1d55aff32a78583f641540eceddf98ee7f5a6f26f0fec49b97f25b03d2d7",
- "dist/2023-10-05/cargo-1.73.0-arm-unknown-linux-gnueabihf.tar.xz": "fa81a1431d26d77e56c73c5c7360f851e6f3c60867f36dfb62088634fcf30f6e",
- "dist/2023-10-05/cargo-1.73.0-armv7-unknown-linux-gnueabihf.tar.gz": "3f4d3e00b72d35681c66158f5e2af85f07916c422dfce62c3d1bcd4c6245e8f1",
- "dist/2023-10-05/cargo-1.73.0-armv7-unknown-linux-gnueabihf.tar.xz": "74ef152b4088eb5ac0808c8e2a02dd399743fc05c607f9e2914fe8b6100d4d24",
- "dist/2023-10-05/cargo-1.73.0-i686-pc-windows-gnu.tar.gz": "2c032fd5dc4bb7ba88cd7e4d00e069f65a8f7987af6f45642f00d3b06c2aaae4",
- "dist/2023-10-05/cargo-1.73.0-i686-pc-windows-gnu.tar.xz": "8e4451380d0b60c2a5f1eedf878de149d526890da6aa778baf8087cb0add3b0c",
- "dist/2023-10-05/cargo-1.73.0-i686-pc-windows-msvc.tar.gz": "ba9af54afb0f4f1e945cb1050810122d45f30ec8bc248979fb13fdd5071ef5e8",
- "dist/2023-10-05/cargo-1.73.0-i686-pc-windows-msvc.tar.xz": "bca12d33241f4f8f97a4b20e971300022c267815cb52daf86208a005aa3d0284",
- "dist/2023-10-05/cargo-1.73.0-i686-unknown-linux-gnu.tar.gz": "df6126fa404c9cd604f48aebc27f6d286957de282c96d84a6edc1c8129e7fd78",
- "dist/2023-10-05/cargo-1.73.0-i686-unknown-linux-gnu.tar.xz": "8780f10eb3565b47f2616ccc1616c1a491a12a055976a25de551cb29e7f50390",
- "dist/2023-10-05/cargo-1.73.0-loongarch64-unknown-linux-gnu.tar.gz": "1a0034cb47fa1e10f5de041fb2e7de87772d22242b660fc07819a64357260214",
- "dist/2023-10-05/cargo-1.73.0-loongarch64-unknown-linux-gnu.tar.xz": "8e4766b19147d99670e5e3473981967d94465ad550c70c830ee86425260e1a05",
- "dist/2023-10-05/cargo-1.73.0-powerpc-unknown-linux-gnu.tar.gz": "ec6b1acb6523da1d0dd26b738c2c180334f478214e7dbf2dcb9b56e85f452189",
- "dist/2023-10-05/cargo-1.73.0-powerpc-unknown-linux-gnu.tar.xz": "ffa30a6e480b3e20cf42ca32266edcfd0b8e4f1b84da450937e4bca7ec3e88e8",
- "dist/2023-10-05/cargo-1.73.0-powerpc64-unknown-linux-gnu.tar.gz": "4ad720bd1f0b21dee6d434d8bc6c8fc196e439a63d21b96469e8311f67254317",
- "dist/2023-10-05/cargo-1.73.0-powerpc64-unknown-linux-gnu.tar.xz": "35454cd3778dac739da6a3fbbe278a2b6818bc4ca95600412e0d51c13f3fdc1f",
- "dist/2023-10-05/cargo-1.73.0-powerpc64le-unknown-linux-gnu.tar.gz": "84492ddbff6353fe5a1630b6e27d713b07df0a8d20f63162dfbd5378e8e997a6",
- "dist/2023-10-05/cargo-1.73.0-powerpc64le-unknown-linux-gnu.tar.xz": "14f74b06ddeebbec48098828d2de55e631ea4cacd7c7ebad8a96220d1a470e0a",
- "dist/2023-10-05/cargo-1.73.0-riscv64gc-unknown-linux-gnu.tar.gz": "a7272b1ee8c47a87c6e9acc93c42c6aab11976ba9f20264ddf38b22d29e96766",
- "dist/2023-10-05/cargo-1.73.0-riscv64gc-unknown-linux-gnu.tar.xz": "50d42893e2da56657a7c00ed17498417628793d8b2bceab829be7b2575478879",
- "dist/2023-10-05/cargo-1.73.0-s390x-unknown-linux-gnu.tar.gz": "ab024854b1b5d91b906cd7c06b867c307cb3d88b159d5b3c4911980650dd6849",
- "dist/2023-10-05/cargo-1.73.0-s390x-unknown-linux-gnu.tar.xz": "6035a925f3307c98d2caf0b1727fc401e7a64a09e6a7132a0cd882937720bda2",
- "dist/2023-10-05/cargo-1.73.0-x86_64-apple-darwin.tar.gz": "1a69a767e0ecd3e4de896c653ff266b6f16400144fe30141eb83f785cd93945b",
- "dist/2023-10-05/cargo-1.73.0-x86_64-apple-darwin.tar.xz": "94f9eb5836fe59a3ef1d1d4c99623d602b0cec48964c5676453be4205df3b28a",
- "dist/2023-10-05/cargo-1.73.0-x86_64-pc-windows-gnu.tar.gz": "843a91924e42ae60b5dc27044980eaf0e873b94638cf81b7fca4fe8dd2ad2e15",
- "dist/2023-10-05/cargo-1.73.0-x86_64-pc-windows-gnu.tar.xz": "349354cfbef7fd74768956a77a4c2ae51c1d1b6037e32e0e236b78046d7b5130",
- "dist/2023-10-05/cargo-1.73.0-x86_64-pc-windows-msvc.tar.gz": "3e43e674ac10a3a1cd68770ca8934fd3e81d0a278c733961a62103a7e0f45f71",
- "dist/2023-10-05/cargo-1.73.0-x86_64-pc-windows-msvc.tar.xz": "8adabe498c4012e4966eb386d5fc623e8dc62be24eb6f8576117546acebdd0ed",
- "dist/2023-10-05/cargo-1.73.0-x86_64-unknown-freebsd.tar.gz": "04708cea978fd354a828a0f5ecc0a596067e8aaf333f75ab80c3409f99242eff",
- "dist/2023-10-05/cargo-1.73.0-x86_64-unknown-freebsd.tar.xz": "6f72439338b48a7492bea15e82052108f1aef43a7524b7b570d58396c47d0e37",
- "dist/2023-10-05/cargo-1.73.0-x86_64-unknown-illumos.tar.gz": "6fb7afe5a3b47ab8ae129dab8cfb5e00d79948b65acbf0caaf6f487a77d67b68",
- "dist/2023-10-05/cargo-1.73.0-x86_64-unknown-illumos.tar.xz": "4ed00c28b689c12818c8e27d8a951d959bc4475c25c2c9ea52aacdd3ec20bc79",
- "dist/2023-10-05/cargo-1.73.0-x86_64-unknown-linux-gnu.tar.gz": "78ad87102aebe101fb61d8fb6bb4b4da8674c57f0af810b3b3310f9f1a63d002",
- "dist/2023-10-05/cargo-1.73.0-x86_64-unknown-linux-gnu.tar.xz": "7c3ce5738d570eaea97dd3d213ea73c8beda4f0c61e7486f95e497b7b10c4e2d",
- "dist/2023-10-05/cargo-1.73.0-x86_64-unknown-linux-musl.tar.gz": "67088e733cd3570ae798d1f26a60eb4144219df894de15da10680456d68a9d14",
- "dist/2023-10-05/cargo-1.73.0-x86_64-unknown-linux-musl.tar.xz": "7e17d4e03d5a949e73d02866589e2010e8a070e025ead08a2030d8d8dc002054",
- "dist/2023-10-05/cargo-1.73.0-x86_64-unknown-netbsd.tar.gz": "69b68d94ab1a4e0819c779cb8751b737acb3d07fd6a28092d8dbea0d757f3e9d",
- "dist/2023-10-05/cargo-1.73.0-x86_64-unknown-netbsd.tar.xz": "36dd97bf0d2bda2eaf4f01b9e6b9af33455efda86bed59c57dbfde3685c98a01",
- "dist/2023-10-05/rust-std-1.73.0-aarch64-apple-darwin.tar.gz": "651d9ccf5282c67b4f5bcf0eb194b0d29750667271144c3921016a018e33e3c5",
- "dist/2023-10-05/rust-std-1.73.0-aarch64-apple-darwin.tar.xz": "25818c9b2ca878076892504bfc9504a24f638be3de23691a548dc094f08c0172",
- "dist/2023-10-05/rust-std-1.73.0-aarch64-apple-ios-sim.tar.gz": "ea324a61b86a1bfc0cc9e777d2f7c466e4862f164d54f897ebe519d0f8418670",
- "dist/2023-10-05/rust-std-1.73.0-aarch64-apple-ios-sim.tar.xz": "3fbe2a9e31f30f6f8c7b106faaf5ca2a1a6db6bfc4a469d5a295985e65f7eb54",
- "dist/2023-10-05/rust-std-1.73.0-aarch64-apple-ios.tar.gz": "1facbc739ff729baaf5770182e5298f0a582180e5b8933f3618c0695d1fe380b",
- "dist/2023-10-05/rust-std-1.73.0-aarch64-apple-ios.tar.xz": "b88b04feb469772d210e20ff2e811f5ca6f0c899e11f37a4e31bb23b3d495989",
- "dist/2023-10-05/rust-std-1.73.0-aarch64-linux-android.tar.gz": "5eaddfcd2094dc402bd36fb9e5ce0d1706f5295fca6cef139c896607d516328f",
- "dist/2023-10-05/rust-std-1.73.0-aarch64-linux-android.tar.xz": "88c2b5a3024f2a52b8470b8f19ab39c1d2efbab3077a27032bcf7bf4c909c7b7",
- "dist/2023-10-05/rust-std-1.73.0-aarch64-pc-windows-msvc.tar.gz": "63e8ef371f6536df347c2a3ccd0a94dd00298fd9762da0e2af48b29a05f841ba",
- "dist/2023-10-05/rust-std-1.73.0-aarch64-pc-windows-msvc.tar.xz": "654b0c7a33f96ed7c94b381b2b4096527532dff7b3b6d8a696f17201f95d2acb",
- "dist/2023-10-05/rust-std-1.73.0-aarch64-unknown-fuchsia.tar.gz": "de39017a43282149d8371009e763cc3a428e28ecac47198a62084ce8a78b72d8",
- "dist/2023-10-05/rust-std-1.73.0-aarch64-unknown-fuchsia.tar.xz": "f30595abbe82afca60fcc96f4db42e7b5466b69018ca590183770704a9b0af82",
- "dist/2023-10-05/rust-std-1.73.0-aarch64-unknown-linux-gnu.tar.gz": "47f2f904befca10a5f6dd68271a343b3700e651c67e25e723d4a4a0e2b4e445b",
- "dist/2023-10-05/rust-std-1.73.0-aarch64-unknown-linux-gnu.tar.xz": "cbaa2549b3accc63b424251fadc3a66d922541df22e736a355246d81998f4252",
- "dist/2023-10-05/rust-std-1.73.0-aarch64-unknown-linux-musl.tar.gz": "ea4a46131c39b27a4d8a74e3ee0d63a70df7995683828f87fad08fccf5592fd8",
- "dist/2023-10-05/rust-std-1.73.0-aarch64-unknown-linux-musl.tar.xz": "bedb1879079a06b6f4fd75caa77ef6919fbcf646d945d4ce4a0cbfd4d3d1411a",
- "dist/2023-10-05/rust-std-1.73.0-aarch64-unknown-none-softfloat.tar.gz": "1f63c641dfc3fbb3beb3189bb3e15a7d82d90362f5b63e78c6c9118ed6bcaf85",
- "dist/2023-10-05/rust-std-1.73.0-aarch64-unknown-none-softfloat.tar.xz": "642de87868928e3ef63b896e54459b23d0fcd3eb7e6966ef3d431ea1eb39e5d0",
- "dist/2023-10-05/rust-std-1.73.0-aarch64-unknown-none.tar.gz": "ec16910716ebf19e3d360f21aed857bf4218cd8b3a783f243c04653f1331485b",
- "dist/2023-10-05/rust-std-1.73.0-aarch64-unknown-none.tar.xz": "1e4b01bcd667f37e93e933feab6cb245a3cb67aa21ebbecd92d773738b68a46c",
- "dist/2023-10-05/rust-std-1.73.0-aarch64-unknown-uefi.tar.gz": "bb5cec68529fcae069c2ae3a3c769b14d3d5f0f1257ada2034b76473546671b4",
- "dist/2023-10-05/rust-std-1.73.0-aarch64-unknown-uefi.tar.xz": "df624b39267712327688302248a334309f9d034dab3a10aa0e6eb3f4f3e97489",
- "dist/2023-10-05/rust-std-1.73.0-arm-linux-androideabi.tar.gz": "85c2b28e3c1dcc694612c8212441bd210b9eb216f9b0e2850e12176f48fc08d1",
- "dist/2023-10-05/rust-std-1.73.0-arm-linux-androideabi.tar.xz": "344907791142ba3d3a73aad6bf7461d1f0467b49a631a56d9c84aa91f5a6989e",
- "dist/2023-10-05/rust-std-1.73.0-arm-unknown-linux-gnueabi.tar.gz": "00b26559bff1532b8c2a2adf9f39447f0d40ee18152382d01dc77a8bbf1a9bad",
- "dist/2023-10-05/rust-std-1.73.0-arm-unknown-linux-gnueabi.tar.xz": "ff3e3c592a3ee552b8c0c4130fb276db3d79bfad2f479c5b64a9cac48e2d0ada",
- "dist/2023-10-05/rust-std-1.73.0-arm-unknown-linux-gnueabihf.tar.gz": "4f2e613aba4053abf0d3e519bc366420922dc48b175f6d30ee449355ca2a786f",
- "dist/2023-10-05/rust-std-1.73.0-arm-unknown-linux-gnueabihf.tar.xz": "5e21a4776d5bf00f9f938da43dcbfd0246932f9d23a5c0e670f917319dc93182",
- "dist/2023-10-05/rust-std-1.73.0-arm-unknown-linux-musleabi.tar.gz": "876d1a2e5e79865c96413592264832bb04b56d5c92b3eb46f56d7b189df24674",
- "dist/2023-10-05/rust-std-1.73.0-arm-unknown-linux-musleabi.tar.xz": "0dac8ae3a12c0f24970225779d3a3a74aa58d61b1e6caff776d27bea3c277a13",
- "dist/2023-10-05/rust-std-1.73.0-arm-unknown-linux-musleabihf.tar.gz": "f10a744d05a39e048794818ead20fa5482ef5001d04262d49a83d9bff8e8dafe",
- "dist/2023-10-05/rust-std-1.73.0-arm-unknown-linux-musleabihf.tar.xz": "e8cb2aa3fe45561ddabac0651169c51458197043beac0a98977740c8933aa4cb",
- "dist/2023-10-05/rust-std-1.73.0-armebv7r-none-eabi.tar.gz": "93ef36b006ccac218ee77ff28c7fcd5774bb4fe228f832d33d7ec77634176d21",
- "dist/2023-10-05/rust-std-1.73.0-armebv7r-none-eabi.tar.xz": "0d3d290d2238b392909e4759e693bbc423c3085845d8190a5d07145c2c7992e9",
- "dist/2023-10-05/rust-std-1.73.0-armebv7r-none-eabihf.tar.gz": "12b941f90848dec3eee75e572e816724bdb4c53784aa7919631f7eff647b7822",
- "dist/2023-10-05/rust-std-1.73.0-armebv7r-none-eabihf.tar.xz": "45e961a4b7e3da572e847edf9c86969c239e84998764912a2110aa9d0fa528c2",
- "dist/2023-10-05/rust-std-1.73.0-armv5te-unknown-linux-gnueabi.tar.gz": "0819db0eb290d52c1c2329a81120bf9c903af14467694c250c5f1051be2faf0d",
- "dist/2023-10-05/rust-std-1.73.0-armv5te-unknown-linux-gnueabi.tar.xz": "1458293c9c03ba145d80e270f6d8c6c88342cac47a4251913e9c935024d32bb2",
- "dist/2023-10-05/rust-std-1.73.0-armv5te-unknown-linux-musleabi.tar.gz": "c0aa0452a3ccdc6eab70b4e6f09a00fae1ae8c205c21d68607d8c70319403ce8",
- "dist/2023-10-05/rust-std-1.73.0-armv5te-unknown-linux-musleabi.tar.xz": "56076706cf46aa207d267fb866c6336318a05a7219eb897a9cab08e270d9b3ba",
- "dist/2023-10-05/rust-std-1.73.0-armv7-linux-androideabi.tar.gz": "2759afc98b442cf0151646763bbc22f8bf4f99653d118d972cc9dd963bfab102",
- "dist/2023-10-05/rust-std-1.73.0-armv7-linux-androideabi.tar.xz": "59fb2c3b4d0cd0383707d48ba787f917cbc64907ef1d994d0756e877a9c09939",
- "dist/2023-10-05/rust-std-1.73.0-armv7-unknown-linux-gnueabi.tar.gz": "5122ce0b8daf1fb06eec01858a098da252bb4fc5811e134b0538ab365a421a2a",
- "dist/2023-10-05/rust-std-1.73.0-armv7-unknown-linux-gnueabi.tar.xz": "3a7a5f702c775c7b331b75e1b4d114c6c30982c21319c15af74bb16bf68fdb74",
- "dist/2023-10-05/rust-std-1.73.0-armv7-unknown-linux-gnueabihf.tar.gz": "d6b645f127517c9553af2eeb919e120a59d84b2b035334a11a734f2ddc7775ca",
- "dist/2023-10-05/rust-std-1.73.0-armv7-unknown-linux-gnueabihf.tar.xz": "a2603ddc66fca8e94ab961c27cca7c7838292be683684b71f47b89d2dd0daee1",
- "dist/2023-10-05/rust-std-1.73.0-armv7-unknown-linux-musleabi.tar.gz": "f28247ee917af2a39369c4e3c64320f63dd4d0396d0e1db6001763a22784412d",
- "dist/2023-10-05/rust-std-1.73.0-armv7-unknown-linux-musleabi.tar.xz": "bced22d26e4614e272efb5dcbe27d724fbc7d8e012ca6b4738ad217ad9f09d0d",
- "dist/2023-10-05/rust-std-1.73.0-armv7-unknown-linux-musleabihf.tar.gz": "b314174907b4f4127e61f8c61580be4dcdd8354dff471c39d6e3573742142402",
- "dist/2023-10-05/rust-std-1.73.0-armv7-unknown-linux-musleabihf.tar.xz": "f6210eb6a81e5a31f7555596a58e010e3d5df1ce4e1caba14fb52daa82da1d30",
- "dist/2023-10-05/rust-std-1.73.0-armv7a-none-eabi.tar.gz": "43908a23e03011e2e4235d3988d3bfab7b149b333026034a3f5605f7fa488643",
- "dist/2023-10-05/rust-std-1.73.0-armv7a-none-eabi.tar.xz": "d17b4ed053164e778dfa99d89c26fe8ec9976129fc29c1cd6c4b65efc3b873eb",
- "dist/2023-10-05/rust-std-1.73.0-armv7r-none-eabi.tar.gz": "ebfd8cd8778a3c5ea7f3308b2da98cc260602218105cebb77ff567718178a182",
- "dist/2023-10-05/rust-std-1.73.0-armv7r-none-eabi.tar.xz": "1a6cc3497d73239e8866405e846b020c4aaf249bbfd81c81a1d16f2e1caaf6b9",
- "dist/2023-10-05/rust-std-1.73.0-armv7r-none-eabihf.tar.gz": "ad78f4221805356cb8277bd2cb2d90fa2d4ce207a97d5e94074b25f4d271e4fd",
- "dist/2023-10-05/rust-std-1.73.0-armv7r-none-eabihf.tar.xz": "90ee8b68bd20b8e8350cee613689773755f887a84e7f0f32899be41e6c5fd6b8",
- "dist/2023-10-05/rust-std-1.73.0-asmjs-unknown-emscripten.tar.gz": "5840125b5f17c582192ac5b6d59429e656c73ec95b52880664d542e5ccac3634",
- "dist/2023-10-05/rust-std-1.73.0-asmjs-unknown-emscripten.tar.xz": "9b050088caa4e56814d2d4c7ee7a60ba1fd8b9811181b816c0c46f84cb88ede0",
- "dist/2023-10-05/rust-std-1.73.0-i586-pc-windows-msvc.tar.gz": "21cd82e7447232c655097a9b939798b8a09ad20b79936b50919e8742cbd88854",
- "dist/2023-10-05/rust-std-1.73.0-i586-pc-windows-msvc.tar.xz": "7e9b0e25c9659eba254cbc56bbfc156bd5d988ce0a56895103b1a1eb950986c8",
- "dist/2023-10-05/rust-std-1.73.0-i586-unknown-linux-gnu.tar.gz": "36d540407f298067b66048101a84551ef819c5d6aa20fbadc81ebf7eb0fa7f52",
- "dist/2023-10-05/rust-std-1.73.0-i586-unknown-linux-gnu.tar.xz": "02cfcb9bae68c406b5cab9aad74ac8631fa4fdd9246aac2e4d4c0aeafbcad42a",
- "dist/2023-10-05/rust-std-1.73.0-i586-unknown-linux-musl.tar.gz": "b22bd5785b56015721dd6ec929201345473aeba6642cb9caee1b4925ce005f8d",
- "dist/2023-10-05/rust-std-1.73.0-i586-unknown-linux-musl.tar.xz": "8e890effc060db56d4768d8a8e5dc8119773e2b015ecf4ad1491f3abb37156b9",
- "dist/2023-10-05/rust-std-1.73.0-i686-linux-android.tar.gz": "11edc3ac59dbad950e8526a48eef238b1c337ac32d2135d9d879bd0e8595321c",
- "dist/2023-10-05/rust-std-1.73.0-i686-linux-android.tar.xz": "b8f026f1e36740a3cc0372fec9ac1b72ed826a75985ba27f9961bd351f9ff270",
- "dist/2023-10-05/rust-std-1.73.0-i686-pc-windows-gnu.tar.gz": "07ec6d91ae4866c13238b3bd81653c0693169d86b31cacf6978e45138525e33d",
- "dist/2023-10-05/rust-std-1.73.0-i686-pc-windows-gnu.tar.xz": "ce72b620b02914bd8cbb4f084a9e01da50d4ae14e089badd961e9a784ae2ab99",
- "dist/2023-10-05/rust-std-1.73.0-i686-pc-windows-msvc.tar.gz": "447ff95ae36a626e47b9b8b21663f1858b3869152ea1dc4b2b979d68bb8fde58",
- "dist/2023-10-05/rust-std-1.73.0-i686-pc-windows-msvc.tar.xz": "0b8515a871cce30a2e887914d3f95379652a0cdcd9339d6ab1648478d8de8f57",
- "dist/2023-10-05/rust-std-1.73.0-i686-unknown-freebsd.tar.gz": "b97901e7d01e1e6386f28bf8d59d09ceed1248368109f09fc02e2ee6de2ae772",
- "dist/2023-10-05/rust-std-1.73.0-i686-unknown-freebsd.tar.xz": "5a4c6818d53497fe011cde3cc0db97194dd33e29a807ef05b72271cf07bc32ba",
- "dist/2023-10-05/rust-std-1.73.0-i686-unknown-linux-gnu.tar.gz": "75f92f0e33c6724cb1876625289126fcf2d101fc6e30ab5a34309e618d6e06a5",
- "dist/2023-10-05/rust-std-1.73.0-i686-unknown-linux-gnu.tar.xz": "c46936bf3c921c90593b6ea77a08b1ec8b240c8184b287fd89fea636ac437b25",
- "dist/2023-10-05/rust-std-1.73.0-i686-unknown-linux-musl.tar.gz": "e76a24f65256ec1d00101ddbd3b91434d7a4029d0e68ba36574b61f7e30056ba",
- "dist/2023-10-05/rust-std-1.73.0-i686-unknown-linux-musl.tar.xz": "661abedfa02937b0a80fb0a9c95c329c6557445e14bce0a38cf1eaa52bd0b2db",
- "dist/2023-10-05/rust-std-1.73.0-i686-unknown-uefi.tar.gz": "855f31d5dbaf03d4d3281a7dacb813a0f803c6c76fe065027a3b59133ff0f167",
- "dist/2023-10-05/rust-std-1.73.0-i686-unknown-uefi.tar.xz": "2a20aa6399db99c9a0ef109c259247d3f5f3d7e93892884101e9ea3168a33d33",
- "dist/2023-10-05/rust-std-1.73.0-loongarch64-unknown-linux-gnu.tar.gz": "6622ad841aa5495302e6f7ad7da491f207bd9b9853770af12ae17b4be2d2bc71",
- "dist/2023-10-05/rust-std-1.73.0-loongarch64-unknown-linux-gnu.tar.xz": "533d38377ea362ba83a3eba5008fb89466f4094b247cb1510d4752d0bbbcc070",
- "dist/2023-10-05/rust-std-1.73.0-mips-unknown-linux-musl.tar.gz": "a022d67fead972a75c06a357e093326c446049129fd8333dcad4842a13b48f5d",
- "dist/2023-10-05/rust-std-1.73.0-mips-unknown-linux-musl.tar.xz": "18077459bb25f288dee41330dfc53990cdb878991346181f44247e8132609988",
- "dist/2023-10-05/rust-std-1.73.0-mips64-unknown-linux-muslabi64.tar.gz": "fb70dd02ecda2983a8ea1a2efa0741cbf70f153dea6b1b76fd30dfd5aadcbcf3",
- "dist/2023-10-05/rust-std-1.73.0-mips64-unknown-linux-muslabi64.tar.xz": "536f17c440c68c566f467a94c762d5ebc604b60c729e55d74cccbd07a4ff848a",
- "dist/2023-10-05/rust-std-1.73.0-mips64el-unknown-linux-muslabi64.tar.gz": "472cf11a105d8a04cb7681ff15d0f640938aff722ac8d6f22c473bc19184f8f0",
- "dist/2023-10-05/rust-std-1.73.0-mips64el-unknown-linux-muslabi64.tar.xz": "cf81ece0565ba8afda0ce59098d8bf208e7d5c0f6d0a26e93c9184e41857ab0c",
- "dist/2023-10-05/rust-std-1.73.0-mipsel-unknown-linux-musl.tar.gz": "c4f0d0ee36a05f8bbe7d95dc495a9ed071589ed2916a64953589829f0744b3c4",
- "dist/2023-10-05/rust-std-1.73.0-mipsel-unknown-linux-musl.tar.xz": "a7a1cc9bb8314b64c50ee69d8722ba4fd2b00344646f0e7783b17d341dd3a10a",
- "dist/2023-10-05/rust-std-1.73.0-nvptx64-nvidia-cuda.tar.gz": "2c9480db356bcda33bb00c3849ee27e29437db8c55fdb00e4cce766702aac63f",
- "dist/2023-10-05/rust-std-1.73.0-nvptx64-nvidia-cuda.tar.xz": "7d18b15f6fb5f33800ba279b761f4acfdb1e199fda04f5d444a42eb1435cc896",
- "dist/2023-10-05/rust-std-1.73.0-powerpc-unknown-linux-gnu.tar.gz": "49692419b05e82adf1099ebc17468f9bc3d411f5d42d39ec77c13473f2b5ea2c",
- "dist/2023-10-05/rust-std-1.73.0-powerpc-unknown-linux-gnu.tar.xz": "bed705726b356d318a0fc34bbf1a1bc4cacb61bfc20889bdfb77d3aaebbd406a",
- "dist/2023-10-05/rust-std-1.73.0-powerpc64-unknown-linux-gnu.tar.gz": "43bb5f678682f1d7e87eca812c8612f900ecb6f8d907def65ea423d96a0b7638",
- "dist/2023-10-05/rust-std-1.73.0-powerpc64-unknown-linux-gnu.tar.xz": "c7bb65f4b7b59f65e4db8a0877192f59dce12021c866e2ef86014d99d18a23f5",
- "dist/2023-10-05/rust-std-1.73.0-powerpc64le-unknown-linux-gnu.tar.gz": "3541ba35884afdc7d88c3fca1fa6bebb9b0dea768592316e842c95d9df0db400",
- "dist/2023-10-05/rust-std-1.73.0-powerpc64le-unknown-linux-gnu.tar.xz": "a74d300dbf1fbf2d4af80995db19501211e2bd25572ab45748b48df4448d4656",
- "dist/2023-10-05/rust-std-1.73.0-riscv32i-unknown-none-elf.tar.gz": "0b1ef8c786331fc829734d8f68308391f260083f2d69c3bac831562e575977ac",
- "dist/2023-10-05/rust-std-1.73.0-riscv32i-unknown-none-elf.tar.xz": "ebca371337af8ebfa291a8bc01a84c791791d92da7a2167810b93eac94a68eb5",
- "dist/2023-10-05/rust-std-1.73.0-riscv32imac-unknown-none-elf.tar.gz": "4bbbb4f474be024e67d3863edcfa72b612ddf8e544827470d4406e19d2e07a20",
- "dist/2023-10-05/rust-std-1.73.0-riscv32imac-unknown-none-elf.tar.xz": "4ae953c4af46a4286f13bd24c67e0a7756265c3ee412ad0aedd1a194e19daf47",
- "dist/2023-10-05/rust-std-1.73.0-riscv32imc-unknown-none-elf.tar.gz": "e92d10ad99d96083d6c54382f449de3f35b816c72ffe341e6453999150193f4d",
- "dist/2023-10-05/rust-std-1.73.0-riscv32imc-unknown-none-elf.tar.xz": "0133978b2cf68c8b9a3e9bc1c01295a4d394998f8801445e854ecfce3f57cb94",
- "dist/2023-10-05/rust-std-1.73.0-riscv64gc-unknown-linux-gnu.tar.gz": "18438669633be274ac50011bd4c8b9ae2d94174c9eb5a219569ad9add7429e23",
- "dist/2023-10-05/rust-std-1.73.0-riscv64gc-unknown-linux-gnu.tar.xz": "8b58ae17468f2f2768b470f532a1ba9668dc778c242cc9dfc022ea1ebc962f63",
- "dist/2023-10-05/rust-std-1.73.0-riscv64gc-unknown-none-elf.tar.gz": "4109e997234957866753504e7f62802fbfa2f9e73e66b75db9f0db84eed54585",
- "dist/2023-10-05/rust-std-1.73.0-riscv64gc-unknown-none-elf.tar.xz": "4bdd977bcfb629731956188cd5670050afdacb3c48265f15a055cb6f701739dd",
- "dist/2023-10-05/rust-std-1.73.0-riscv64imac-unknown-none-elf.tar.gz": "98b944ff9738a1760c679e639f9fa134253f5f115af5b52c878e41a6b246bc30",
- "dist/2023-10-05/rust-std-1.73.0-riscv64imac-unknown-none-elf.tar.xz": "53303c963ba923ef4e57838e49fe617dfcf5fce084893413e0a9f4dd541bc105",
- "dist/2023-10-05/rust-std-1.73.0-s390x-unknown-linux-gnu.tar.gz": "537c6e932cafbdbfb8c5365dee7b80bff69b4fd7aa4426c4997f1aa9839ad1e3",
- "dist/2023-10-05/rust-std-1.73.0-s390x-unknown-linux-gnu.tar.xz": "545b97978470135f8726fb82970acb882b9d57a724c2462ee90efee47429fc84",
- "dist/2023-10-05/rust-std-1.73.0-sparc64-unknown-linux-gnu.tar.gz": "d611d45bc18e95f88d62231602cea88c4f818649a0ac99e6043042da59e11482",
- "dist/2023-10-05/rust-std-1.73.0-sparc64-unknown-linux-gnu.tar.xz": "717846830d95a689fb44dbafc4f4e09285bf7bc2c37bcc70f6d17785c21f4569",
- "dist/2023-10-05/rust-std-1.73.0-sparcv9-sun-solaris.tar.gz": "6c2c503eb925badce557ae47e29d808ce1b842d009b69a371fa387685ad05106",
- "dist/2023-10-05/rust-std-1.73.0-sparcv9-sun-solaris.tar.xz": "856dd0969ad1de3631ebaab7dd0bee4d4bb57df9fad7b94750fb3a2a8d3d191f",
- "dist/2023-10-05/rust-std-1.73.0-thumbv6m-none-eabi.tar.gz": "b9c232a7926feb3704feb14903829eabc71ba2910657a314115f7bca1041330b",
- "dist/2023-10-05/rust-std-1.73.0-thumbv6m-none-eabi.tar.xz": "47a6e78841c9e1b0f4cd11251a9ee6aaa8c42e5c491a41842738befe84d3467a",
- "dist/2023-10-05/rust-std-1.73.0-thumbv7em-none-eabi.tar.gz": "1003f9923c56efe846504b890ebfa442fac08b898fc8d0d4dc5ee4d3a717baf9",
- "dist/2023-10-05/rust-std-1.73.0-thumbv7em-none-eabi.tar.xz": "ccc60a7d07010a3414112adccc5e46958333d54ad8c8282e11b8be8aa1d08d9c",
- "dist/2023-10-05/rust-std-1.73.0-thumbv7em-none-eabihf.tar.gz": "9a5c605744f5132c89d8270cfca662e4cb39a5e43f86348fff8ef712a553321d",
- "dist/2023-10-05/rust-std-1.73.0-thumbv7em-none-eabihf.tar.xz": "b5d8eed543402f4f147c4a1848517089f56d0bed9dd42404efb3f7feca3aa322",
- "dist/2023-10-05/rust-std-1.73.0-thumbv7m-none-eabi.tar.gz": "289cd380e185675598e45645d8d12d97a45f02cd2cbf900ff36d3587314f04a1",
- "dist/2023-10-05/rust-std-1.73.0-thumbv7m-none-eabi.tar.xz": "a62b36d3c510324666d11cf138cf9c15f6e1c35f6738bc4fc2aff711be747dc6",
- "dist/2023-10-05/rust-std-1.73.0-thumbv7neon-linux-androideabi.tar.gz": "329af84c913b3a84a137542ca0a42a537ff7046ba0397639dd6725b3068b2cae",
- "dist/2023-10-05/rust-std-1.73.0-thumbv7neon-linux-androideabi.tar.xz": "4ede4a8ba3c56f95fa33fb3ceac7e8488d7dd248a83786b76e5d9f5fedd3c2ea",
- "dist/2023-10-05/rust-std-1.73.0-thumbv7neon-unknown-linux-gnueabihf.tar.gz": "12a862561090e64606bcdb0fee285a4316b6fffc7f231b36f700e69e8e99a3fb",
- "dist/2023-10-05/rust-std-1.73.0-thumbv7neon-unknown-linux-gnueabihf.tar.xz": "3417c665a0b8fa532b93ceddaa5a0cf540b3d6d76cc6717ec1888e76e363c491",
- "dist/2023-10-05/rust-std-1.73.0-thumbv8m.base-none-eabi.tar.gz": "b47e09c25c012545bee76ace1a634148c6f8301937481e3d1d9c864ba84b4cfc",
- "dist/2023-10-05/rust-std-1.73.0-thumbv8m.base-none-eabi.tar.xz": "2402e54f11bca414f6627b92cfc9ca011ac1e141a95e73c48c320eb2c9bf2585",
- "dist/2023-10-05/rust-std-1.73.0-thumbv8m.main-none-eabi.tar.gz": "a4886e73b84ee1d65db545d55165f74df16dc2276ba59e3c87e60fb672f7865f",
- "dist/2023-10-05/rust-std-1.73.0-thumbv8m.main-none-eabi.tar.xz": "52086549988734b2568c39061521e989592d144bc5ba7a737d7e7fb69c19e429",
- "dist/2023-10-05/rust-std-1.73.0-thumbv8m.main-none-eabihf.tar.gz": "451033af0c2526b55614d1f271bf10e059c5c3acc202e8f7aaa98cb90e486b3e",
- "dist/2023-10-05/rust-std-1.73.0-thumbv8m.main-none-eabihf.tar.xz": "5ad7fbeb80a020976e0a314eb72f860c530019608111abda455a5d8775a68cb0",
- "dist/2023-10-05/rust-std-1.73.0-wasm32-unknown-emscripten.tar.gz": "3e1a17c71d3eaf113d6d09b0a5560eda7541c29c71d694d25daa25dbdc936cfa",
- "dist/2023-10-05/rust-std-1.73.0-wasm32-unknown-emscripten.tar.xz": "4158dbfa17168685b1334eece9feccd0c0be4650770b218cceb8e0119ec7baf0",
- "dist/2023-10-05/rust-std-1.73.0-wasm32-unknown-unknown.tar.gz": "f5b840b93f00db0785c32f5193b6e3492fe4c626e148a5ebffe07184b6435104",
- "dist/2023-10-05/rust-std-1.73.0-wasm32-unknown-unknown.tar.xz": "466336ff685a0d864b0aa8f6ac2d2c242eae6c67ad746857e523b9351eee2a37",
- "dist/2023-10-05/rust-std-1.73.0-wasm32-wasi-preview1-threads.tar.gz": "69fb83a5438b9b3dabc4d4603960877153af067b907d5e2c038e69725ccad5e8",
- "dist/2023-10-05/rust-std-1.73.0-wasm32-wasi-preview1-threads.tar.xz": "64671338897356fbc302279ab788b3af6b5e525c81dddf97148b285351ab1c86",
- "dist/2023-10-05/rust-std-1.73.0-wasm32-wasi.tar.gz": "d439a6c28eea3a1972acd2b66d943048ac22a81e4389d50b2a1d7612d75c550c",
- "dist/2023-10-05/rust-std-1.73.0-wasm32-wasi.tar.xz": "fa4d9105f9da44f8c5852335e52378bda44c3edaafdd11ec4c672c39f00d7d5b",
- "dist/2023-10-05/rust-std-1.73.0-x86_64-apple-darwin.tar.gz": "1726086f74e6348a95286dde2810c5da6f9591cd5989f38178026fcd4b720b9c",
- "dist/2023-10-05/rust-std-1.73.0-x86_64-apple-darwin.tar.xz": "18055852bd8c0cc325bc94847a814678e98703a0301b46497dafc6cef7aa2f8d",
- "dist/2023-10-05/rust-std-1.73.0-x86_64-apple-ios.tar.gz": "95f8300e5421141f9f9a8f45b866106724e00d9f307d7b880a046cddce5f73d4",
- "dist/2023-10-05/rust-std-1.73.0-x86_64-apple-ios.tar.xz": "698a4afd48f154d7bbd224198f332491b1d34af6c86dc6e37bd5a862b4fc4032",
- "dist/2023-10-05/rust-std-1.73.0-x86_64-fortanix-unknown-sgx.tar.gz": "8fce2acee651ef36195c2e2b075877c153e1d14f8f83c2d764eb78ff1b90b2af",
- "dist/2023-10-05/rust-std-1.73.0-x86_64-fortanix-unknown-sgx.tar.xz": "6e2b43723a386e5de74eb2c9f61eba966f4534ff2b00a0e925eb861b80416808",
- "dist/2023-10-05/rust-std-1.73.0-x86_64-linux-android.tar.gz": "2c07f989cfb5596b69c70681049337299298ab0823c8a06f099545317460520d",
- "dist/2023-10-05/rust-std-1.73.0-x86_64-linux-android.tar.xz": "a0bb99d568c7a9ffe56917fca64c8c2536e07316498aeb2b8f4c72cc03590f2e",
- "dist/2023-10-05/rust-std-1.73.0-x86_64-pc-solaris.tar.gz": "6d4f1be71630d038eeb61c071270ee0075f712528e683e9b07a873cb287d7bc5",
- "dist/2023-10-05/rust-std-1.73.0-x86_64-pc-solaris.tar.xz": "ba15bd84d3635c8ed2912595f5ee79b6ce0d09059a671e14b420bb8d20e1079d",
- "dist/2023-10-05/rust-std-1.73.0-x86_64-pc-windows-gnu.tar.gz": "e896226c1b9807b6f4b059a30e2a85d26d808b53f13a2e28fd5c77d60d32aa18",
- "dist/2023-10-05/rust-std-1.73.0-x86_64-pc-windows-gnu.tar.xz": "3e957e062a607b317f55bb72efe4265e22d3ed0897b227f74c403cbb42b9025f",
- "dist/2023-10-05/rust-std-1.73.0-x86_64-pc-windows-msvc.tar.gz": "ea7da24247ce6d39bb3fd3fa7f4b50b297d06d53ebfee8c97c5393399963a907",
- "dist/2023-10-05/rust-std-1.73.0-x86_64-pc-windows-msvc.tar.xz": "5e50b20108e036e8176bef4223a09adea310253ea774003b64154bee6e83464c",
- "dist/2023-10-05/rust-std-1.73.0-x86_64-sun-solaris.tar.gz": "603bf41e057484c277aaf1e5687fbcade0b7b219fc46946a007459b1ade317b4",
- "dist/2023-10-05/rust-std-1.73.0-x86_64-sun-solaris.tar.xz": "2b7ea5c7d4fe47b63ea7c6abe88f426decdaa6319b659a0ebe034d31bec2728f",
- "dist/2023-10-05/rust-std-1.73.0-x86_64-unknown-freebsd.tar.gz": "44856b9d812c0b66be580da51d668d3b802c7513bf79a23ee9e56ca9dd56ae84",
- "dist/2023-10-05/rust-std-1.73.0-x86_64-unknown-freebsd.tar.xz": "c34d6635b714f56a040532e05f0949a1bf2dd1ea105c12a591dc338db97a5e29",
- "dist/2023-10-05/rust-std-1.73.0-x86_64-unknown-fuchsia.tar.gz": "4cc3ebd6eb9dcb86c7b5c5f5bcc6b4f6f1f90c47716424b89b1fa46519459362",
- "dist/2023-10-05/rust-std-1.73.0-x86_64-unknown-fuchsia.tar.xz": "fdd2258f3515e170faacec7bfeacc20e807f691fe715413487e8a371f6306f40",
- "dist/2023-10-05/rust-std-1.73.0-x86_64-unknown-illumos.tar.gz": "7d963975b386c9aec11fb9a98297ec4d96812adad9275b9334e53fa40273203d",
- "dist/2023-10-05/rust-std-1.73.0-x86_64-unknown-illumos.tar.xz": "88a3500c8050b1a3dd65c02e1d71edd674dca90b2b3f3508fcc9ff67a13da906",
- "dist/2023-10-05/rust-std-1.73.0-x86_64-unknown-linux-gnu.tar.gz": "9e941972c8679c2d852addf979455afd61e3ec33000cbc2421b162bcb05897a6",
- "dist/2023-10-05/rust-std-1.73.0-x86_64-unknown-linux-gnu.tar.xz": "96efb163a57b400152c357be0ea3a0dd902b56cc0df662b9ac951403c7c7b15b",
- "dist/2023-10-05/rust-std-1.73.0-x86_64-unknown-linux-gnux32.tar.gz": "1cac7e5c3f51357b0f9ccd1b890b3f9ec98512130f47b4a8fee84ea7edfacc55",
- "dist/2023-10-05/rust-std-1.73.0-x86_64-unknown-linux-gnux32.tar.xz": "7538151d6051c2d81947e23756ad944e6bfdecbef656afcc272f8c16a62896b7",
- "dist/2023-10-05/rust-std-1.73.0-x86_64-unknown-linux-musl.tar.gz": "3a0fd7481cf66c4d2e237e07a4b84887efe6099cdcb948c7cd7409ebd56d54be",
- "dist/2023-10-05/rust-std-1.73.0-x86_64-unknown-linux-musl.tar.xz": "c89f5164edffbda4a21212c1e74260021634bda43fbe6d5e4fe9f7e85a79320c",
- "dist/2023-10-05/rust-std-1.73.0-x86_64-unknown-netbsd.tar.gz": "6c65b43d365aede858cbea7b4bc3ae3ca922a6c84ef2a1a80d7a057bd238a871",
- "dist/2023-10-05/rust-std-1.73.0-x86_64-unknown-netbsd.tar.xz": "56a7e9a6040246b592a22299cff485e59ffd7edaacd4214b7fa1382f0f4ab39e",
- "dist/2023-10-05/rust-std-1.73.0-x86_64-unknown-none.tar.gz": "0eccf976aad1b951b92effaa4ca6c62bc0e0a065ab63a13c597bb7a09027464e",
- "dist/2023-10-05/rust-std-1.73.0-x86_64-unknown-none.tar.xz": "159f55c4957bc39d3ab0900ca8fdb50d7fe03f659e984bb9411108478d669765",
- "dist/2023-10-05/rust-std-1.73.0-x86_64-unknown-redox.tar.gz": "acb10b1ecba550c6c41804c0deae2643375f0c83bbc692d23c0e6e6286c2724a",
- "dist/2023-10-05/rust-std-1.73.0-x86_64-unknown-redox.tar.xz": "c0d0a93a8003a4096b745eeccd338e79262c59ae0e6292d80673c868ffbfca29",
- "dist/2023-10-05/rust-std-1.73.0-x86_64-unknown-uefi.tar.gz": "f9fb7f7cbe6d4279cefb7e9715dd41b6b086b8fed010a405b4d824fb31d1a7f2",
- "dist/2023-10-05/rust-std-1.73.0-x86_64-unknown-uefi.tar.xz": "eab427bfcdcedb896d77f38f43a19b7d7fe7eed4d1d333da7cb35cb5304dfa6e",
- "dist/2023-10-05/rustc-1.73.0-aarch64-apple-darwin.tar.gz": "f5c938b2aedaf3451e41b696875aab7f66c435d8245af3af1f61ec636b0e64ee",
- "dist/2023-10-05/rustc-1.73.0-aarch64-apple-darwin.tar.xz": "a0cb60d97d18064a816fc4e1d55faef51aec807606fc467b99451355584e5763",
- "dist/2023-10-05/rustc-1.73.0-aarch64-pc-windows-msvc.tar.gz": "b763325d37471e502daa7be88b811f2c80eed52c51161a917ce1397161b8ecde",
- "dist/2023-10-05/rustc-1.73.0-aarch64-pc-windows-msvc.tar.xz": "83330743feed9dc75b4e98cf46d447ac89afb7e50aec17ece47f4268201f3fc1",
- "dist/2023-10-05/rustc-1.73.0-aarch64-unknown-linux-gnu.tar.gz": "5f7141617b833f84a279b19e7c349b95e839d924e2a3ed3ae545b2d4ab55ce05",
- "dist/2023-10-05/rustc-1.73.0-aarch64-unknown-linux-gnu.tar.xz": "628f57a45eb8143a7ac1acd5d6d01e3ae3cdf1ad11d151795ed765f6e5f3047e",
- "dist/2023-10-05/rustc-1.73.0-aarch64-unknown-linux-musl.tar.gz": "26daef406cbde47dbd3d4ce463c66d91a25b8243eac31309735a9daf17ec9dd4",
- "dist/2023-10-05/rustc-1.73.0-aarch64-unknown-linux-musl.tar.xz": "efd94902e11e173714160e44dc843af42e0c7b8820c41851982497bc9a616763",
- "dist/2023-10-05/rustc-1.73.0-arm-unknown-linux-gnueabi.tar.gz": "099d9511c34f062b0db7614fa23af1552d2ecabcbe58e31c32bc329529d7088f",
- "dist/2023-10-05/rustc-1.73.0-arm-unknown-linux-gnueabi.tar.xz": "6f6ab466fd67cefd7eaa2ceaa339cd80bfcc693c1b6b2c9fc3cc3cb893fa0850",
- "dist/2023-10-05/rustc-1.73.0-arm-unknown-linux-gnueabihf.tar.gz": "9f6eb085a1795cf897b831e5c507b126fead0e5286e524307112a92942b84aa8",
- "dist/2023-10-05/rustc-1.73.0-arm-unknown-linux-gnueabihf.tar.xz": "117a67a06e74de1ad386b340a50332ffd5cce3aac58d07b972091c94eee10d51",
- "dist/2023-10-05/rustc-1.73.0-armv7-unknown-linux-gnueabihf.tar.gz": "31ba70b4cefeccf2a2300fc46fbfd3fc1c82239e483161da5c840f5130df9d65",
- "dist/2023-10-05/rustc-1.73.0-armv7-unknown-linux-gnueabihf.tar.xz": "d326ee86d9acb29543053692f41cddd31a9f89cb3e25f58a57a38635d6c93532",
- "dist/2023-10-05/rustc-1.73.0-i686-pc-windows-gnu.tar.gz": "9d2b55ad39289e646fdee02adddad49f7eee195eb62d14955bce7520cd2aebf5",
- "dist/2023-10-05/rustc-1.73.0-i686-pc-windows-gnu.tar.xz": "a4546c61ce631e4fb45188f54682278f1813b068fadff4dabc4f209ba1592ce8",
- "dist/2023-10-05/rustc-1.73.0-i686-pc-windows-msvc.tar.gz": "f7061c8789fc54329bea6f6188d7feff07eddc7776393ce0163dab49af2fe637",
- "dist/2023-10-05/rustc-1.73.0-i686-pc-windows-msvc.tar.xz": "0b92409bec5c59d5bf5c34c9bf0717464d94ce1f8acd626a43cb2951c47744c7",
- "dist/2023-10-05/rustc-1.73.0-i686-unknown-linux-gnu.tar.gz": "5399791a11a3d8617f680f02fc7f1c14fba1a0d27a1cc3a256e31678853763a1",
- "dist/2023-10-05/rustc-1.73.0-i686-unknown-linux-gnu.tar.xz": "3d98a27c50ae79de7c7956506d649e169312e76b4fc2314a975b45ebd1dea5b9",
- "dist/2023-10-05/rustc-1.73.0-loongarch64-unknown-linux-gnu.tar.gz": "33e92b9bf79856887d043d75ed679d71e85dcd19800f10f90503254ed4da2353",
- "dist/2023-10-05/rustc-1.73.0-loongarch64-unknown-linux-gnu.tar.xz": "0fee28452598859780cf871d3f5a809ca606a840c843a8735248c49dbbd32b1c",
- "dist/2023-10-05/rustc-1.73.0-powerpc-unknown-linux-gnu.tar.gz": "871f3de34a538c365bddb18da149aea11ae8b2bb270a65df842c694fcc4dc8f9",
- "dist/2023-10-05/rustc-1.73.0-powerpc-unknown-linux-gnu.tar.xz": "2ed995d178158b8447b68c8642cffed2402fc7502c0d5475b7c9fdfa895dd037",
- "dist/2023-10-05/rustc-1.73.0-powerpc64-unknown-linux-gnu.tar.gz": "0cc7bd1af776d29242fb7c7110629f956eab7fb6631d9bebcdffb7a8a3dcf5b9",
- "dist/2023-10-05/rustc-1.73.0-powerpc64-unknown-linux-gnu.tar.xz": "9458ea1308e88ceb88dbc38d9d4918dc665d3c148a39789de417b0668fc45a3f",
- "dist/2023-10-05/rustc-1.73.0-powerpc64le-unknown-linux-gnu.tar.gz": "50e85c6d60fb5e61b58ce51451849d4d8b882a72faf68647719e39b1c3cf9cf7",
- "dist/2023-10-05/rustc-1.73.0-powerpc64le-unknown-linux-gnu.tar.xz": "c52d6f28d370e7bd30e07655d534a3aad21afc6c32f0c80e8a0f7249d2b86b29",
- "dist/2023-10-05/rustc-1.73.0-riscv64gc-unknown-linux-gnu.tar.gz": "2e546d836fccce65edc50acc3b00216bda25fe5d9da012fec9f9d2ca3dff4133",
- "dist/2023-10-05/rustc-1.73.0-riscv64gc-unknown-linux-gnu.tar.xz": "fee4b06850574de4821d335e622ca0607753e042848ba00ada826f8c8ca4b44a",
- "dist/2023-10-05/rustc-1.73.0-s390x-unknown-linux-gnu.tar.gz": "564dd4d3b647eed655bebf9c1e3540bd6d411bec5b5c65a07281b192a5a20068",
- "dist/2023-10-05/rustc-1.73.0-s390x-unknown-linux-gnu.tar.xz": "0ca1d450f10f2d87b630c5a19f3aad13f0e39aec63f253654ad9f68c7bf1872e",
- "dist/2023-10-05/rustc-1.73.0-x86_64-apple-darwin.tar.gz": "4ef3199cbdb16f1001db1fcb880c1cc0c8a898b915f03faed7e5d1d553ceaf83",
- "dist/2023-10-05/rustc-1.73.0-x86_64-apple-darwin.tar.xz": "eb2aefe3251967ceb9278a05317d3731e60f2c65b81feb0d74abe65d80683a54",
- "dist/2023-10-05/rustc-1.73.0-x86_64-pc-windows-gnu.tar.gz": "b34895c82c7dac52054830b32a1e9856b734c6538959637727a4251810241742",
- "dist/2023-10-05/rustc-1.73.0-x86_64-pc-windows-gnu.tar.xz": "95b11b8f8c96f7b2e4e0530fb36b9d28e0f6c71bfeb2b8509b87cc7cb09dfb99",
- "dist/2023-10-05/rustc-1.73.0-x86_64-pc-windows-msvc.tar.gz": "86236a424befed9bcc93192dc1fb0ef83398460e94381834d095bae7cc61951b",
- "dist/2023-10-05/rustc-1.73.0-x86_64-pc-windows-msvc.tar.xz": "be819e0210df90598fcc80bd3e80fef815a0e7c9363b7e130b1d133f4f5e6686",
- "dist/2023-10-05/rustc-1.73.0-x86_64-unknown-freebsd.tar.gz": "2649e2612aefdda63f8ac70118632229b7739f863bf5f80d3ef4324ceab3add2",
- "dist/2023-10-05/rustc-1.73.0-x86_64-unknown-freebsd.tar.xz": "e706d75704bc8b900ba7efaad5696e7b905ba6436175c6b58d0bf6854409d21a",
- "dist/2023-10-05/rustc-1.73.0-x86_64-unknown-illumos.tar.gz": "53f8ff1bcf0834e861d0d0cc719561c4ce24c3cf8da9d3c0b9c10ee59d0eeee6",
- "dist/2023-10-05/rustc-1.73.0-x86_64-unknown-illumos.tar.xz": "ab4854508c4dab64602779f269cf3f42ce13d80ce76aca6a6d66383014e25934",
- "dist/2023-10-05/rustc-1.73.0-x86_64-unknown-linux-gnu.tar.gz": "31be7397a8a70fcb48e119925c9ff05554e2094140889ef9760b70a724d56346",
- "dist/2023-10-05/rustc-1.73.0-x86_64-unknown-linux-gnu.tar.xz": "14f383eb4d6e65ce01cc99f2c5cf5a78744239f29704f72fe84f11095af779f5",
- "dist/2023-10-05/rustc-1.73.0-x86_64-unknown-linux-musl.tar.gz": "406511c06def9be54dadbb738b748f93f7c991423a3f8b7ce47a131c5da0f0b2",
- "dist/2023-10-05/rustc-1.73.0-x86_64-unknown-linux-musl.tar.xz": "e885398a10f5f4697dfb2b4e399bf9a0805ee8c11b9f9365ba4d803fef34d779",
- "dist/2023-10-05/rustc-1.73.0-x86_64-unknown-netbsd.tar.gz": "7bc775b8edbb7bc3868a1a6e74725d3d335bea4d0eb93396d00d24fbcf6c6c33",
- "dist/2023-10-05/rustc-1.73.0-x86_64-unknown-netbsd.tar.xz": "e2976be295c1d96b95b1c2c8e2226965ea3d25dac4b8e33566481cc86c2edabe"
+ "dist/2023-11-16/cargo-1.74.0-aarch64-apple-darwin.tar.gz": "b7e4c2a829bd8bc90101067ba96b71ecd73fe130401b2478b095047bd8acb469",
+ "dist/2023-11-16/cargo-1.74.0-aarch64-apple-darwin.tar.xz": "5c14e9b3a458d728d89e02f4e024b710d5b0eb8c45249066fe666d2094fbf233",
+ "dist/2023-11-16/cargo-1.74.0-aarch64-pc-windows-msvc.tar.gz": "b8f88749a925da82bf96e0c63f1489d138f43b4441c205d157da2492c8df5261",
+ "dist/2023-11-16/cargo-1.74.0-aarch64-pc-windows-msvc.tar.xz": "878a50562c0f6c44d66bb4319f9c9a23ff81f41afabd79f88527659e6822efea",
+ "dist/2023-11-16/cargo-1.74.0-aarch64-unknown-linux-gnu.tar.gz": "ab22b5aa6baa622a267f98ef2f1d06dd5a4a95b7ca6cadb0c431d31f1e018251",
+ "dist/2023-11-16/cargo-1.74.0-aarch64-unknown-linux-gnu.tar.xz": "a18dc9132cf76ccba90bcbb53b56a4d37ebfb34845f61e79f7b5d4710a269647",
+ "dist/2023-11-16/cargo-1.74.0-aarch64-unknown-linux-musl.tar.gz": "fcd459888913ca0960496469ab3450413b085fe2bcd2c9e45aeaf32c992fd6d4",
+ "dist/2023-11-16/cargo-1.74.0-aarch64-unknown-linux-musl.tar.xz": "c810330480f62a118db9c9d35f9953de63c2817ae3145b198fd17d77cd021c79",
+ "dist/2023-11-16/cargo-1.74.0-arm-unknown-linux-gnueabi.tar.gz": "c4a1f294e4efb2c14f52424c7cb781a3855493718bc6fe6ef443951f0fb32da8",
+ "dist/2023-11-16/cargo-1.74.0-arm-unknown-linux-gnueabi.tar.xz": "7ac55018d33a13cd83af74879a2f9e2317f6f1d4d82f2d0f63612591e84120d4",
+ "dist/2023-11-16/cargo-1.74.0-arm-unknown-linux-gnueabihf.tar.gz": "498320f83a3f4426aadb55422f7546158d8008ba218633b630101d259830b60b",
+ "dist/2023-11-16/cargo-1.74.0-arm-unknown-linux-gnueabihf.tar.xz": "e650c511248386c3cda6f9a14b3b7b3aca7571f0018ab72df8c9f092bb4fb1f2",
+ "dist/2023-11-16/cargo-1.74.0-armv7-unknown-linux-gnueabihf.tar.gz": "88127c7a75c931f6f2601a49693cddf76e3824ba41dc1a60039946cfdeda4702",
+ "dist/2023-11-16/cargo-1.74.0-armv7-unknown-linux-gnueabihf.tar.xz": "b2d9ee1c3ce4ddb3c7e8af6125d7440e5d16d67c5304007c0a7688bdf97d5c6e",
+ "dist/2023-11-16/cargo-1.74.0-i686-pc-windows-gnu.tar.gz": "8bc61e1972033304dbf9acdd9364d2a9e8959cac66debad6d0e962abf6094fc4",
+ "dist/2023-11-16/cargo-1.74.0-i686-pc-windows-gnu.tar.xz": "06d8db81d12eb46040e37b80a7cce497cee28d7bd077e4d095922dc29bfcd484",
+ "dist/2023-11-16/cargo-1.74.0-i686-pc-windows-msvc.tar.gz": "5a439f9b15bdf49711f3aeab387c606693a2ab07d92362d6d72dfe0955dc03d4",
+ "dist/2023-11-16/cargo-1.74.0-i686-pc-windows-msvc.tar.xz": "79dca52ceae75434c6ea011562d1e9f5ea4c9bc3faf7f9c56cbf09e4b8f36f5f",
+ "dist/2023-11-16/cargo-1.74.0-i686-unknown-linux-gnu.tar.gz": "cfb97ce4725c464621605d8b44753536032752843ff8cde52915a935e17a19ed",
+ "dist/2023-11-16/cargo-1.74.0-i686-unknown-linux-gnu.tar.xz": "9f5b5226a69f95950a381ec5bb15dde7a90865a6df8aa0b470082a40d42d9f38",
+ "dist/2023-11-16/cargo-1.74.0-loongarch64-unknown-linux-gnu.tar.gz": "7bacb1145c13263519b6186b38ed550b6fb459d14eab08fe6ee11807cd305987",
+ "dist/2023-11-16/cargo-1.74.0-loongarch64-unknown-linux-gnu.tar.xz": "77d6d55122150d8fc56d31fb166fd1b2ae48bff7376459c1b0030727fc604998",
+ "dist/2023-11-16/cargo-1.74.0-powerpc-unknown-linux-gnu.tar.gz": "659df71587b953b2373f29f9a8c455be9d08e021ef69b450c0f4f628497554a8",
+ "dist/2023-11-16/cargo-1.74.0-powerpc-unknown-linux-gnu.tar.xz": "08ea8a345839f34d26f21b94ed6d458e6a38513999f7ddc05175c371983e6deb",
+ "dist/2023-11-16/cargo-1.74.0-powerpc64-unknown-linux-gnu.tar.gz": "073cbd5650754c71056cb3d4e37c2d46fae5ac8f4b17c5789ad95cfa78b055b4",
+ "dist/2023-11-16/cargo-1.74.0-powerpc64-unknown-linux-gnu.tar.xz": "696863642318f139634e6856f5e946ea970318ce79d4d9b1595871a70a662a89",
+ "dist/2023-11-16/cargo-1.74.0-powerpc64le-unknown-linux-gnu.tar.gz": "a941b1242012c55e804078d0eba2c4054daf872650a7a6f58f550a6a5f4ffa6f",
+ "dist/2023-11-16/cargo-1.74.0-powerpc64le-unknown-linux-gnu.tar.xz": "2eccd404aabe5137a8e45b6173c27d08862a0e674d5866be71aff1434f271d50",
+ "dist/2023-11-16/cargo-1.74.0-riscv64gc-unknown-linux-gnu.tar.gz": "4b077103ca3d11e1a5a321f868eda1f279202fe4cb1cd2db390cbd730c3524ee",
+ "dist/2023-11-16/cargo-1.74.0-riscv64gc-unknown-linux-gnu.tar.xz": "5b224e465e006b5fe959ad64d0df0540c4318ba4e39edd89794d520eef60b026",
+ "dist/2023-11-16/cargo-1.74.0-s390x-unknown-linux-gnu.tar.gz": "243a17a817771331774f6200eddbadc714cc848b603be395e67739e28e4b9987",
+ "dist/2023-11-16/cargo-1.74.0-s390x-unknown-linux-gnu.tar.xz": "06267377c811271d6e4ba6feea1d4b84a9f4c5c8d1dbd46092d0a0595f24e9b6",
+ "dist/2023-11-16/cargo-1.74.0-x86_64-apple-darwin.tar.gz": "f45dec402a07acf072f1f58064cb3d21cd795914182e8260d88fce73f082b577",
+ "dist/2023-11-16/cargo-1.74.0-x86_64-apple-darwin.tar.xz": "5c1c4f5985a48ad02bcff881c5a9c983218bc1eefc083403579147a3292ba073",
+ "dist/2023-11-16/cargo-1.74.0-x86_64-pc-windows-gnu.tar.gz": "866e57b04f186afbc0a46b938ff751fbd2c3c708cfde27616302fe09887e9954",
+ "dist/2023-11-16/cargo-1.74.0-x86_64-pc-windows-gnu.tar.xz": "22f3ca584a35e13e513b13b4a6879f3b80830672a2cd5482ddf1878b0980ccac",
+ "dist/2023-11-16/cargo-1.74.0-x86_64-pc-windows-msvc.tar.gz": "67a24036f287ef93de5d185ec0c0a99b17a20e11d17bbe1113160476cfc72cc2",
+ "dist/2023-11-16/cargo-1.74.0-x86_64-pc-windows-msvc.tar.xz": "d589666945922759a594cb0c26cd678dc7efaa60e8b148b63ff280f0e3887ba0",
+ "dist/2023-11-16/cargo-1.74.0-x86_64-unknown-freebsd.tar.gz": "a84d6c84e5831031d4cef1be2217fc10a6e3e336e8691f74a62719cd957255b2",
+ "dist/2023-11-16/cargo-1.74.0-x86_64-unknown-freebsd.tar.xz": "10db2451549ac0081e34a222178fcba71b36bd64f3156005e8392e4b7d36dd0f",
+ "dist/2023-11-16/cargo-1.74.0-x86_64-unknown-illumos.tar.gz": "ab1a41d467166245076c9e4ed09dfdea7d6bab55b10fc045bac4d76aefccbf71",
+ "dist/2023-11-16/cargo-1.74.0-x86_64-unknown-illumos.tar.xz": "d6d83a18ccef5b211a1a80aa5437e25cce7bd61cc4680aa92c5f7ba67e871788",
+ "dist/2023-11-16/cargo-1.74.0-x86_64-unknown-linux-gnu.tar.gz": "38451abcf728c8583cba29dbd74debf56ce585dcc829ac7b03ccf94a563b8ddf",
+ "dist/2023-11-16/cargo-1.74.0-x86_64-unknown-linux-gnu.tar.xz": "f219386d4569c40b660518e99267afff428c13bf980bda7a614c8d4038d013f6",
+ "dist/2023-11-16/cargo-1.74.0-x86_64-unknown-linux-musl.tar.gz": "857e817701b86e45ba02a400fa2a6d755373141c90ca52bd8b0a9fc4736766a1",
+ "dist/2023-11-16/cargo-1.74.0-x86_64-unknown-linux-musl.tar.xz": "773f9be04a0de37fb6931e9806d3668512d5ce76062434da24a2c7f71426d244",
+ "dist/2023-11-16/cargo-1.74.0-x86_64-unknown-netbsd.tar.gz": "7042b4d35d3f8abbfcbc819b13ad766a2b3cfce8ad81255f746d25a8f7516d34",
+ "dist/2023-11-16/cargo-1.74.0-x86_64-unknown-netbsd.tar.xz": "2903590cf343d55ba8c1f7fa725bb55c2761cde37701b5753d7cd7ece5efaaef",
+ "dist/2023-11-16/rust-std-1.74.0-aarch64-apple-darwin.tar.gz": "5c02396eb7cfe1a3c12b01dffd758cf862c4264df6280727798745b98f245082",
+ "dist/2023-11-16/rust-std-1.74.0-aarch64-apple-darwin.tar.xz": "7c8e78df45a915a770bb38096042dbeb8154557bf56f8bd565354991ea0a64fb",
+ "dist/2023-11-16/rust-std-1.74.0-aarch64-apple-ios-sim.tar.gz": "cf20808c5c4a23188702415cd20e1d702f6fa96b29e7bd2aff32b7cafc164f29",
+ "dist/2023-11-16/rust-std-1.74.0-aarch64-apple-ios-sim.tar.xz": "1c15440336300a910d96ca3a7a4ad7ca5838d74d994b91b7c30220c2607bb1e8",
+ "dist/2023-11-16/rust-std-1.74.0-aarch64-apple-ios.tar.gz": "bd9a94ecb2d8713674221298c57ad3e752c583c192de5d4c9b8250b3683c0340",
+ "dist/2023-11-16/rust-std-1.74.0-aarch64-apple-ios.tar.xz": "ef3544335f7c80fb7370b62e3c4bcc5e261093e24d979843f93d1cd2920f1a4b",
+ "dist/2023-11-16/rust-std-1.74.0-aarch64-linux-android.tar.gz": "da3adaa22a6dc853dc491a21ca1e8d4d1a5b9dabc73e086a298ea5c6a07dd1cb",
+ "dist/2023-11-16/rust-std-1.74.0-aarch64-linux-android.tar.xz": "3c33942a80cf60acc94cefc1365c93168962d5f8d3d718bac52f94aa73ebef26",
+ "dist/2023-11-16/rust-std-1.74.0-aarch64-pc-windows-msvc.tar.gz": "ff7ad83ba45b2c6eacfdbe3cfbe77a8c836d2ddc99fdc14709a28b117a7e36d8",
+ "dist/2023-11-16/rust-std-1.74.0-aarch64-pc-windows-msvc.tar.xz": "db3c9fdf9bf1e923d353d3f0bb1181671b99f4b4fb61cafb029d23cd48f330cb",
+ "dist/2023-11-16/rust-std-1.74.0-aarch64-unknown-fuchsia.tar.gz": "97051b26c5c112b6b603ee5720b91cda0771e3d3116c0f65f87dee5303eb7646",
+ "dist/2023-11-16/rust-std-1.74.0-aarch64-unknown-fuchsia.tar.xz": "98bb47f1b767de5d82b02cca9d21f447933973c795dfc237c2f02f5bf3f23403",
+ "dist/2023-11-16/rust-std-1.74.0-aarch64-unknown-linux-gnu.tar.gz": "56df7a51381bdf38ceba057c93581d00aab4619d78974bca9f47cbc49aa8497b",
+ "dist/2023-11-16/rust-std-1.74.0-aarch64-unknown-linux-gnu.tar.xz": "c5ad01692bc08ce6f4db2ac815be63498b45013380c71f22b3d33bf3be767270",
+ "dist/2023-11-16/rust-std-1.74.0-aarch64-unknown-linux-musl.tar.gz": "b1007bfb4dd53f34b49d0c15a0c0d910edff0333c778ee69eb2ef9ce1d4d5972",
+ "dist/2023-11-16/rust-std-1.74.0-aarch64-unknown-linux-musl.tar.xz": "9feab5d7390309596a55f156d4154a7b9a743c36847fae46ae79df455946977b",
+ "dist/2023-11-16/rust-std-1.74.0-aarch64-unknown-none-softfloat.tar.gz": "87b09e72be584486a79c0b5338b80b6d3304218c9f492f7549bbdccdf78458d6",
+ "dist/2023-11-16/rust-std-1.74.0-aarch64-unknown-none-softfloat.tar.xz": "c1bb5e95831068f877eeabe8537d933270de5eb20ef7b7a2b3efdc346fafad28",
+ "dist/2023-11-16/rust-std-1.74.0-aarch64-unknown-none.tar.gz": "f09339f07a47de458f85916456ea938e83acdd88fa52230a2c0ff9330e649512",
+ "dist/2023-11-16/rust-std-1.74.0-aarch64-unknown-none.tar.xz": "a7c83585a9fd4f172452e231fe29bbf48e2a539789121e66170e7b0c468f35b7",
+ "dist/2023-11-16/rust-std-1.74.0-aarch64-unknown-uefi.tar.gz": "7a735be4421f61a1852608c059b009b06f356d0842802efd359af9421bd01cd3",
+ "dist/2023-11-16/rust-std-1.74.0-aarch64-unknown-uefi.tar.xz": "88e04b8ceb64a88111dd3e5f4048e9cd0aa7280bd562ede50f497c12333c5d4a",
+ "dist/2023-11-16/rust-std-1.74.0-arm-linux-androideabi.tar.gz": "cb3046a92875e5682e5501b4f39d223f0bd59f383e7e41833c69b88abb7d80e1",
+ "dist/2023-11-16/rust-std-1.74.0-arm-linux-androideabi.tar.xz": "200ec3a6ebe1299582df25fa159e86733dfdf697372ca932adf0d56ea98b4d80",
+ "dist/2023-11-16/rust-std-1.74.0-arm-unknown-linux-gnueabi.tar.gz": "7c0976345a96aaf4160007bc0c0d381968db3927d6c19b5a6e244aad1a870fa5",
+ "dist/2023-11-16/rust-std-1.74.0-arm-unknown-linux-gnueabi.tar.xz": "4c9b4f7770fdf7b1398e6216d3cd3a67d5f1ea6519e013bf2d6da69a252dc56e",
+ "dist/2023-11-16/rust-std-1.74.0-arm-unknown-linux-gnueabihf.tar.gz": "cad78e22d6f0eb87f385776c53d1783bdd0702c08f52f2cdea106207e099dedc",
+ "dist/2023-11-16/rust-std-1.74.0-arm-unknown-linux-gnueabihf.tar.xz": "8c493e78f14e9468d2a270a8ce0e4447d68924be692415aa1882c1173fd2163d",
+ "dist/2023-11-16/rust-std-1.74.0-arm-unknown-linux-musleabi.tar.gz": "4558c072a1a9250a5cc6a46227bfc138553317b313578a64c59f339c44688019",
+ "dist/2023-11-16/rust-std-1.74.0-arm-unknown-linux-musleabi.tar.xz": "5b43234042ab22fa30e35f2eadf693d48149bcd2a2fc3c577fc6bdf8e0886409",
+ "dist/2023-11-16/rust-std-1.74.0-arm-unknown-linux-musleabihf.tar.gz": "ed7695839a670f31507f7955a266a6340702bf69d0bc4b8da2e6da994ec1bbb0",
+ "dist/2023-11-16/rust-std-1.74.0-arm-unknown-linux-musleabihf.tar.xz": "1b240fbafe6d6a92c288d39812971a78848750089810edaccb7edb20ff18035d",
+ "dist/2023-11-16/rust-std-1.74.0-armebv7r-none-eabi.tar.gz": "b84eadcebddfbdda96ea123aab2381b0db1f19f7abb3ddcbefabd61de715b68b",
+ "dist/2023-11-16/rust-std-1.74.0-armebv7r-none-eabi.tar.xz": "922920c18f4c798c43c86a15561f77430ff43677a7b766af5469853588edec19",
+ "dist/2023-11-16/rust-std-1.74.0-armebv7r-none-eabihf.tar.gz": "b70ed2d10a627f0525b4bed3b3d0d0a417c965b46849384f7bd574d9790fdd5a",
+ "dist/2023-11-16/rust-std-1.74.0-armebv7r-none-eabihf.tar.xz": "333344b4f13319c9733fcd694992f7a9fedf87aa4e537f0e3222bab3dd6ed8fc",
+ "dist/2023-11-16/rust-std-1.74.0-armv5te-unknown-linux-gnueabi.tar.gz": "2b3c675dcda15a87fd071f271d5acd9b01db6c449864986d44dd7a3b8491e2c1",
+ "dist/2023-11-16/rust-std-1.74.0-armv5te-unknown-linux-gnueabi.tar.xz": "9bb3ed52b3a2a0c578bca00ebb13c2d0fe3ac38150f08721552c6a61d744a2af",
+ "dist/2023-11-16/rust-std-1.74.0-armv5te-unknown-linux-musleabi.tar.gz": "9802f3132727bfb8aa6371684ce355ff6dad0d8c7cbe2146a65d5b4dca616c01",
+ "dist/2023-11-16/rust-std-1.74.0-armv5te-unknown-linux-musleabi.tar.xz": "dcf8da600f3b570453caae5204691e6e112fc6900e6a6b96698777da3b0870c4",
+ "dist/2023-11-16/rust-std-1.74.0-armv7-linux-androideabi.tar.gz": "f97c5bb9edab43e4bd21787a6e078f978a356dc6af0be790fcb20cb170f24724",
+ "dist/2023-11-16/rust-std-1.74.0-armv7-linux-androideabi.tar.xz": "d6c6382bfe5c8dc0a0d1c7614e9aa80f5563ac17e4d79b08a9028746a3005a8a",
+ "dist/2023-11-16/rust-std-1.74.0-armv7-unknown-linux-gnueabi.tar.gz": "9310611d09c74263bdc4beba03e04ed1e2e789fa29ba042688934d3a80890b02",
+ "dist/2023-11-16/rust-std-1.74.0-armv7-unknown-linux-gnueabi.tar.xz": "0a62db7d162d220c1de4c583b3c942338bc6cfb3c326143468641aa69515a046",
+ "dist/2023-11-16/rust-std-1.74.0-armv7-unknown-linux-gnueabihf.tar.gz": "381f63d4571b7e1326cd62e0d4f2bca177192235cbfb05df63df13d3ab630ca5",
+ "dist/2023-11-16/rust-std-1.74.0-armv7-unknown-linux-gnueabihf.tar.xz": "4c354f2c5bdf6e3ac7fb2ebe2c7e294df64e96931d1ffa118076945e5aeb0b3d",
+ "dist/2023-11-16/rust-std-1.74.0-armv7-unknown-linux-musleabi.tar.gz": "9ca6537729533b3738a2195dd9a2b9a0a47a431c4f7a0c8a6eec01b715868e70",
+ "dist/2023-11-16/rust-std-1.74.0-armv7-unknown-linux-musleabi.tar.xz": "ab70c62e6f2646701e97218a884e64340b015eb02ccd92213fc8c0b6b17d9edd",
+ "dist/2023-11-16/rust-std-1.74.0-armv7-unknown-linux-musleabihf.tar.gz": "b3bb8e7db622f39079721afb68db392d7995404afc2ed6ed3d8d32444d076d37",
+ "dist/2023-11-16/rust-std-1.74.0-armv7-unknown-linux-musleabihf.tar.xz": "7af54fe5950bab20c0af69f363880252049147664bddb692e0a355dcfdf35056",
+ "dist/2023-11-16/rust-std-1.74.0-armv7a-none-eabi.tar.gz": "83dcbd02e5ccdc69a552788fffb1e52a45da94715cc444a79c40f7155f0babad",
+ "dist/2023-11-16/rust-std-1.74.0-armv7a-none-eabi.tar.xz": "2eceb0c14475a5f4b2b1805b2f550bcab787d4a6253bee74bcbefbb1bc3b200b",
+ "dist/2023-11-16/rust-std-1.74.0-armv7r-none-eabi.tar.gz": "7f6d4e8b024b044fb9f8c1d8475b3d6aa9c7c58e78ba7ee76db3d9833eadc997",
+ "dist/2023-11-16/rust-std-1.74.0-armv7r-none-eabi.tar.xz": "d88e65b5efc75132077c37f62d5c85f6bc383f67280fccfb005eaacda691876c",
+ "dist/2023-11-16/rust-std-1.74.0-armv7r-none-eabihf.tar.gz": "2f4318a26d597caee25ba9d91aa56e62c55f97e308ff5ca38a42ad0213d1d346",
+ "dist/2023-11-16/rust-std-1.74.0-armv7r-none-eabihf.tar.xz": "b52f67deaf7e2522b5d7041a67f206827cf1cff97241a040f9d477c5fe8ff67a",
+ "dist/2023-11-16/rust-std-1.74.0-asmjs-unknown-emscripten.tar.gz": "dcd780a44706d4f1f51c1f3bcf2fb8690fdce929c5b9d90a8a3b82a38882da34",
+ "dist/2023-11-16/rust-std-1.74.0-asmjs-unknown-emscripten.tar.xz": "1472dee38ca5bc9148e242d904b6844cdfeab25e532162374fc554aede00c76d",
+ "dist/2023-11-16/rust-std-1.74.0-i586-pc-windows-msvc.tar.gz": "45d5e97ba2e2322fb4f03a39c8f2e55573513a0e177c6ff5998950560fedb1ed",
+ "dist/2023-11-16/rust-std-1.74.0-i586-pc-windows-msvc.tar.xz": "c4371f361c25def6f687f1d6a2fe5da0d73620f4d0866890215adaa3ad36741e",
+ "dist/2023-11-16/rust-std-1.74.0-i586-unknown-linux-gnu.tar.gz": "a317d4f0e6c1906dc6dfcfb8163d432faeb5a647ca7646d2d0174a07b3a5069b",
+ "dist/2023-11-16/rust-std-1.74.0-i586-unknown-linux-gnu.tar.xz": "bd4502462c5e2b2617b23f28862e544f14c4d02658f6d331f0cfbbba914aa4c0",
+ "dist/2023-11-16/rust-std-1.74.0-i586-unknown-linux-musl.tar.gz": "aa1a9b451f17947232d202f84a74ed1aef491496ee14c7e2a35765d2a20f7d13",
+ "dist/2023-11-16/rust-std-1.74.0-i586-unknown-linux-musl.tar.xz": "bb685f4b55f508a18a16db53384a45791228a792cf551bdaeaa5bdfab0706577",
+ "dist/2023-11-16/rust-std-1.74.0-i686-linux-android.tar.gz": "257630f77f94aeb90122f66259d73bab1ec09208c4f7f07c849ec97b0a8e6ebe",
+ "dist/2023-11-16/rust-std-1.74.0-i686-linux-android.tar.xz": "b5f40a60623f51b9f43c579585b91bff9e4fa7bccb55c2274ffedf9dbb222b0d",
+ "dist/2023-11-16/rust-std-1.74.0-i686-pc-windows-gnu.tar.gz": "c5fd89a39dffa9e85ed52be1754bec2d97cb02a9f1cb5b781c6d38716141cd54",
+ "dist/2023-11-16/rust-std-1.74.0-i686-pc-windows-gnu.tar.xz": "85f79bd7b92c4d1563264188a867105ab4b5f0af6f9df562bfb723e82423445d",
+ "dist/2023-11-16/rust-std-1.74.0-i686-pc-windows-msvc.tar.gz": "0bebb9ccec4352a069796fed96cb9476fcba49b290c9e754c36f82ad22216a4d",
+ "dist/2023-11-16/rust-std-1.74.0-i686-pc-windows-msvc.tar.xz": "84608e070c0b03963cc21316e16cd9151360d75d47ee95898e52019d82f162f1",
+ "dist/2023-11-16/rust-std-1.74.0-i686-unknown-freebsd.tar.gz": "16bdbf8a2deffd5d1dc14e678a9335ac61f43444011b7829ad7f9a3d21fa837f",
+ "dist/2023-11-16/rust-std-1.74.0-i686-unknown-freebsd.tar.xz": "3d06461d1a1240c2cee5ea07168cc0bbe9e1f61c29402e82dda9ba03b025ca43",
+ "dist/2023-11-16/rust-std-1.74.0-i686-unknown-linux-gnu.tar.gz": "028e55ccab1b41c8ed54c3a3f63660efbb8cf7e9fd4d7d357fefd14f12ac700c",
+ "dist/2023-11-16/rust-std-1.74.0-i686-unknown-linux-gnu.tar.xz": "69757b72def9c433753e8bb575c817fc1ba389cf1a9c25276db1491ec025e495",
+ "dist/2023-11-16/rust-std-1.74.0-i686-unknown-linux-musl.tar.gz": "e512c406339c95900adae4d4462f50201711cb481fe46bef543c77a5cf06800a",
+ "dist/2023-11-16/rust-std-1.74.0-i686-unknown-linux-musl.tar.xz": "58c60a6b1ef949c4bb819ed7ff907a5823d732b085df8b8980911bf271ef52ce",
+ "dist/2023-11-16/rust-std-1.74.0-i686-unknown-uefi.tar.gz": "cc2c387af66707ae0391006aab923be9d6f0af832cc022b350fcb0a4aa1c5b65",
+ "dist/2023-11-16/rust-std-1.74.0-i686-unknown-uefi.tar.xz": "15ad2fc1e31e8e4ffc30a1a5710141267cce1d8b985ed89a0562d96fbd2eca66",
+ "dist/2023-11-16/rust-std-1.74.0-loongarch64-unknown-linux-gnu.tar.gz": "0554cae752018a3bc10d7f626f8f9e19f3a315331692f34e1c3096f5d0b5a42a",
+ "dist/2023-11-16/rust-std-1.74.0-loongarch64-unknown-linux-gnu.tar.xz": "13b85a882e912d0d8b3228feb5c263d34ec353d483c9defbd3e6bba38935553b",
+ "dist/2023-11-16/rust-std-1.74.0-loongarch64-unknown-none-softfloat.tar.gz": "e48e5a19109701bc3eb5dceb4a4ad7e1c490d8cc5f9742d50e4038079fa6b75c",
+ "dist/2023-11-16/rust-std-1.74.0-loongarch64-unknown-none-softfloat.tar.xz": "db86d0bf81b55d8477905c6e6e619885660be8ee6dfa1c75af9c025bbb4dd088",
+ "dist/2023-11-16/rust-std-1.74.0-loongarch64-unknown-none.tar.gz": "79629570c4b5ed3de5524dd9f6dd66fc15db9939525723367a3d4081bf5c1f37",
+ "dist/2023-11-16/rust-std-1.74.0-loongarch64-unknown-none.tar.xz": "660df16b904d1dc3dae946458d7d6b3c135f9d0352aafe1cdd804524f7930780",
+ "dist/2023-11-16/rust-std-1.74.0-mips-unknown-linux-musl.tar.gz": "9a1d5916df614f7006e1806d9ac01c83e4cfc9169c049041079511741c7cace4",
+ "dist/2023-11-16/rust-std-1.74.0-mips-unknown-linux-musl.tar.xz": "ee2b609a45e492b1d3269f8eec98ba682ad3cf0b720ef5c4bc826c8f3f215aa6",
+ "dist/2023-11-16/rust-std-1.74.0-mips64-unknown-linux-muslabi64.tar.gz": "432c21e9b200e40ab6ab6bb20b6465d8e7cebec2bbd771fc7f52407db261e0ff",
+ "dist/2023-11-16/rust-std-1.74.0-mips64-unknown-linux-muslabi64.tar.xz": "d002409f439b65a859cd9978d2c91ce5d6d1588296ee865396facb95cec1fdd5",
+ "dist/2023-11-16/rust-std-1.74.0-mips64el-unknown-linux-muslabi64.tar.gz": "247de2b317e44aed0c1a11efc933e927392e35abd0f4c8c938195bb156aded22",
+ "dist/2023-11-16/rust-std-1.74.0-mips64el-unknown-linux-muslabi64.tar.xz": "801dbd1e03d6193b6a386050c09f660b9e643b8b0a7cb373e3cdbaf1e3a9dfd3",
+ "dist/2023-11-16/rust-std-1.74.0-mipsel-unknown-linux-musl.tar.gz": "78e31c57fdfac37e57e73f1c1f4d701f6f7f20e12c52fcd8d8567702e231b7c9",
+ "dist/2023-11-16/rust-std-1.74.0-mipsel-unknown-linux-musl.tar.xz": "9eac139849c1d5ac604d3989534e8e5bc83e4102756e6bc3df449fc45e5cd025",
+ "dist/2023-11-16/rust-std-1.74.0-nvptx64-nvidia-cuda.tar.gz": "cf7954078846e81f348642b4b945f09edb673b9a748c4452aecd7b6e60445d90",
+ "dist/2023-11-16/rust-std-1.74.0-nvptx64-nvidia-cuda.tar.xz": "f21d94cb0b5257611b5c829dbb28883566f11c4e4e37fa7202d74bbc3cc12c26",
+ "dist/2023-11-16/rust-std-1.74.0-powerpc-unknown-linux-gnu.tar.gz": "d8a9322fb6c1be2ac52bffff089cbeb72b7da5d4d7d274c8935fc8de520db7b3",
+ "dist/2023-11-16/rust-std-1.74.0-powerpc-unknown-linux-gnu.tar.xz": "458ee056fbeccf1cf96c20506654e5e9104c4e8f23d46cd4bb9b97ff5b3f4d55",
+ "dist/2023-11-16/rust-std-1.74.0-powerpc64-unknown-linux-gnu.tar.gz": "d9af666399e37d2efa2e13f78092a12d52e30b8b6056938164ae7c1e5f3b533f",
+ "dist/2023-11-16/rust-std-1.74.0-powerpc64-unknown-linux-gnu.tar.xz": "7ec56629b7d887753ce3a895fb73b77d2d395acac30207c2b69237ef63279872",
+ "dist/2023-11-16/rust-std-1.74.0-powerpc64le-unknown-linux-gnu.tar.gz": "35b8bedf59c56c23187ce7a9ffc55461ad6c2d7750cc19ecf768c1533e4d5895",
+ "dist/2023-11-16/rust-std-1.74.0-powerpc64le-unknown-linux-gnu.tar.xz": "785956d68855de18546c87d6d06cd2505cb8a10edba84327bf2b448420a31d55",
+ "dist/2023-11-16/rust-std-1.74.0-riscv32i-unknown-none-elf.tar.gz": "f9e2e13315a0eafe9f9bcaae7d3ce2741f9c1ae73f999233081133539bfb427b",
+ "dist/2023-11-16/rust-std-1.74.0-riscv32i-unknown-none-elf.tar.xz": "829f6d5ab504528250501e3ee394cef734dd59b6f23c4e6efb846eb738c5d4bc",
+ "dist/2023-11-16/rust-std-1.74.0-riscv32imac-unknown-none-elf.tar.gz": "fd67d8c83ee25e8fddfc35788a9ef03f25c9a3fea9d47f13d2c3b084b80e943d",
+ "dist/2023-11-16/rust-std-1.74.0-riscv32imac-unknown-none-elf.tar.xz": "3b317cb395124fb022a70dc6971f0aed4f3d554f18c84d8dea54f75a91328538",
+ "dist/2023-11-16/rust-std-1.74.0-riscv32imc-unknown-none-elf.tar.gz": "b81115d06097e9bc728dd1a49fd0ba672e5a4e9315c76b55e16c16779e6c6b60",
+ "dist/2023-11-16/rust-std-1.74.0-riscv32imc-unknown-none-elf.tar.xz": "9f91f3ca3de647974f08f6424eafc669ffde64ad7118aa0f7ee25bcca9634e14",
+ "dist/2023-11-16/rust-std-1.74.0-riscv64gc-unknown-linux-gnu.tar.gz": "67f23b2fd2981e9d83d26506a2222b7a6ea5f89d42786ad6fcabdde89c46c546",
+ "dist/2023-11-16/rust-std-1.74.0-riscv64gc-unknown-linux-gnu.tar.xz": "2a500156825dde03a53c965e5764a440b1ebce973b8a31f21e8bd8104271d56e",
+ "dist/2023-11-16/rust-std-1.74.0-riscv64gc-unknown-none-elf.tar.gz": "6311fbb49d4a0c5c78fda03ce8dfbb74ce0eab50bdea27545a47bb3f4de343f1",
+ "dist/2023-11-16/rust-std-1.74.0-riscv64gc-unknown-none-elf.tar.xz": "afe17e30a254e65eb25d3ea1f0a917e62897ff60d2de8e63a54822ada06c8c69",
+ "dist/2023-11-16/rust-std-1.74.0-riscv64imac-unknown-none-elf.tar.gz": "4c1cbc713ac847d3c7d573f4a60f2c7eec71dccc55f0d454cd15d414a6105799",
+ "dist/2023-11-16/rust-std-1.74.0-riscv64imac-unknown-none-elf.tar.xz": "727bef70754f1c8967b066228f620bc34a70f17339d1cae6a9d1654dd1fd0060",
+ "dist/2023-11-16/rust-std-1.74.0-s390x-unknown-linux-gnu.tar.gz": "e9450e672487e894396daf43a91fabe98cb10288f835dad458771a3858670068",
+ "dist/2023-11-16/rust-std-1.74.0-s390x-unknown-linux-gnu.tar.xz": "35142541b88a1244c8225c64ee18585446d7e67841a9335ccaa95acf2d34dde5",
+ "dist/2023-11-16/rust-std-1.74.0-sparc64-unknown-linux-gnu.tar.gz": "0e4a56453b33fd6f1ae1a79189af9262fe772c18749a5d44541a7344960a295a",
+ "dist/2023-11-16/rust-std-1.74.0-sparc64-unknown-linux-gnu.tar.xz": "68e96875ca7fc6ed0e023fcf752f28b95e9cc7d9881af4e8e167259fdaec7168",
+ "dist/2023-11-16/rust-std-1.74.0-sparcv9-sun-solaris.tar.gz": "96007eb63c3dfef8c16f7b44381e8906c8a9f9611d7f29b748df7c333b3fdfcb",
+ "dist/2023-11-16/rust-std-1.74.0-sparcv9-sun-solaris.tar.xz": "e202c28f8a9af4f714673159ccebacb53f31cf26b0121394bb2b11ee26dd7868",
+ "dist/2023-11-16/rust-std-1.74.0-thumbv6m-none-eabi.tar.gz": "6cd1a4a2616cac22038c7205045e00da34927405940193ee8107eab00da02909",
+ "dist/2023-11-16/rust-std-1.74.0-thumbv6m-none-eabi.tar.xz": "2a08162b19b310ce52f2779174fd1450bb58b1e18e721ca757a21cbf5d94e0d2",
+ "dist/2023-11-16/rust-std-1.74.0-thumbv7em-none-eabi.tar.gz": "36d82db0e991d9d8cce0d4c3885d3b4b5234325218fb0ad20f9b7acb5b50902b",
+ "dist/2023-11-16/rust-std-1.74.0-thumbv7em-none-eabi.tar.xz": "0944319c168a5811b92d9d0cb063e24f1a2e9c743847c82ec12cf717d2a1fa2b",
+ "dist/2023-11-16/rust-std-1.74.0-thumbv7em-none-eabihf.tar.gz": "78f995d61fd97632f14b7821db2648cd3a7a3893a068e57977af6dcba891ef4d",
+ "dist/2023-11-16/rust-std-1.74.0-thumbv7em-none-eabihf.tar.xz": "6df42fc8a28f5e6a3f6b7eb7ae84b080ebd7bb28cae03d4603d67d18a76081fa",
+ "dist/2023-11-16/rust-std-1.74.0-thumbv7m-none-eabi.tar.gz": "07e1c79df0a73090980a7241c69dbfd1ca429d3f5721ae71a4a74af1b47ec895",
+ "dist/2023-11-16/rust-std-1.74.0-thumbv7m-none-eabi.tar.xz": "ce190a2bd5b68cadf57be5ca7eb897601af8196f26a32c4671e5106dee91ddba",
+ "dist/2023-11-16/rust-std-1.74.0-thumbv7neon-linux-androideabi.tar.gz": "1e13b51ed3a2c50571ccf99aeec5d58ddc8c29419d79d116468cf75b80aefdee",
+ "dist/2023-11-16/rust-std-1.74.0-thumbv7neon-linux-androideabi.tar.xz": "8c5f15ab3de41157ebd9bcfa499509be902ed104d272e58c9905411ea17b4f96",
+ "dist/2023-11-16/rust-std-1.74.0-thumbv7neon-unknown-linux-gnueabihf.tar.gz": "91617edb6ba928be3a3c58b0a039339ec7cafc6883ab76d8329b92fb740c2eb2",
+ "dist/2023-11-16/rust-std-1.74.0-thumbv7neon-unknown-linux-gnueabihf.tar.xz": "699271e2ad34dbaae0073cfcdeb60d9cb1c42352dbca8d6fa5a93ecef37dd3f7",
+ "dist/2023-11-16/rust-std-1.74.0-thumbv8m.base-none-eabi.tar.gz": "6f6f9ffdbcb2914c83d60e31ef5d445f69cabcb8eb53d4ab561723081eb9624f",
+ "dist/2023-11-16/rust-std-1.74.0-thumbv8m.base-none-eabi.tar.xz": "1697a61047a8281d8806dfeda695ad0a1c08dd2a35f067f1ed1911d99f701095",
+ "dist/2023-11-16/rust-std-1.74.0-thumbv8m.main-none-eabi.tar.gz": "a09609766adae62d186a53453dcc508289f4f55ba5db8a0e044290eeb200d93c",
+ "dist/2023-11-16/rust-std-1.74.0-thumbv8m.main-none-eabi.tar.xz": "0ed040c707968abfc410c66d8dbd2fa5f6b2972adefa92fa484328361592533e",
+ "dist/2023-11-16/rust-std-1.74.0-thumbv8m.main-none-eabihf.tar.gz": "75b507bfdd871d7e32eb9163f66a017973d1ff16f5fb8e5610d0230d9612e179",
+ "dist/2023-11-16/rust-std-1.74.0-thumbv8m.main-none-eabihf.tar.xz": "d998248816fe437d6bb508fd893d447ba73044cd618c236ba19af0a589cd580e",
+ "dist/2023-11-16/rust-std-1.74.0-wasm32-unknown-emscripten.tar.gz": "32917d4166caa23fb5ea4f6b3771c5c08766db41dd4f68d0235b112c8ea33265",
+ "dist/2023-11-16/rust-std-1.74.0-wasm32-unknown-emscripten.tar.xz": "9930d23b747ce9860a4555f611b562d04f053fb28a1d5f5f3f3e2feb1a6ebdc3",
+ "dist/2023-11-16/rust-std-1.74.0-wasm32-unknown-unknown.tar.gz": "638e818db424fcc66bacb24972da089af78a0ba75d77557b2869d8ef886cd8e1",
+ "dist/2023-11-16/rust-std-1.74.0-wasm32-unknown-unknown.tar.xz": "5333cc5c8d9aa106e29c742f307aab66ad652e5513025a3a164bcd951bbaa92f",
+ "dist/2023-11-16/rust-std-1.74.0-wasm32-wasi-preview1-threads.tar.gz": "dcb2aae9862ab68ce48f77b0f2df25f3659294a5b513b1ba6ad30ed9fc48a706",
+ "dist/2023-11-16/rust-std-1.74.0-wasm32-wasi-preview1-threads.tar.xz": "2bced9b87d818003357670d715247d57ee25e924fee470da1a1a072417f9dcc0",
+ "dist/2023-11-16/rust-std-1.74.0-wasm32-wasi.tar.gz": "b42e4888edd33b824b447ef3ad6b643991c91c7f60735a9f7a474d946ac88bfb",
+ "dist/2023-11-16/rust-std-1.74.0-wasm32-wasi.tar.xz": "d87f75df5e52ee9886cd1bb5542f24f928e702781aab45a2892b3ae39dfc6b62",
+ "dist/2023-11-16/rust-std-1.74.0-x86_64-apple-darwin.tar.gz": "ffd3de3b29d324d7c8b8b57569c11bf3749fc6313ec0b2638ef38997bdbdb6fc",
+ "dist/2023-11-16/rust-std-1.74.0-x86_64-apple-darwin.tar.xz": "72d74d4ce1e8fc308f63d12e344ab745db6969af37b505146c9cc423b2a86450",
+ "dist/2023-11-16/rust-std-1.74.0-x86_64-apple-ios.tar.gz": "1db49035e1163a38317edd9b1eac85f8676710294ac7ce10d34c865324079c42",
+ "dist/2023-11-16/rust-std-1.74.0-x86_64-apple-ios.tar.xz": "7e7275f1ee2b9143943d57c061eaf071c66e6fb37a59a5d663a37a81864301cd",
+ "dist/2023-11-16/rust-std-1.74.0-x86_64-fortanix-unknown-sgx.tar.gz": "b01c1737ddfd139f95bbe68ff15a4ca9c89ea5c72e63a6bc1d27448f5047a9d4",
+ "dist/2023-11-16/rust-std-1.74.0-x86_64-fortanix-unknown-sgx.tar.xz": "b5de46c7af5f89a2644e6594c25839393e617bf5b29eb4e94c7a1012f7d53e96",
+ "dist/2023-11-16/rust-std-1.74.0-x86_64-linux-android.tar.gz": "92083939ea58bde10d597f020cbe7e747b0e8229ba3653e33900cbfe02c658fa",
+ "dist/2023-11-16/rust-std-1.74.0-x86_64-linux-android.tar.xz": "95e55b8ee91ef724efa9ca177c712bb8cd947d593fb9582c9f298d88e6c314b3",
+ "dist/2023-11-16/rust-std-1.74.0-x86_64-pc-solaris.tar.gz": "29c2f089981cf3e5270e735522c701578cd3a0d832ecc54d21d20c8075d5d07b",
+ "dist/2023-11-16/rust-std-1.74.0-x86_64-pc-solaris.tar.xz": "8a970ec743a1aa3026552ffd6d9602e527bc46721e661dc5c08909801bdb4216",
+ "dist/2023-11-16/rust-std-1.74.0-x86_64-pc-windows-gnu.tar.gz": "cc7087ce2987f0c7e25b599412ac53f702640b37508dd1ec0fea8c89e6e63fab",
+ "dist/2023-11-16/rust-std-1.74.0-x86_64-pc-windows-gnu.tar.xz": "a553afddac2dcd8875df1d75e1570c28faff56a5442a2099c09808e6d6959b83",
+ "dist/2023-11-16/rust-std-1.74.0-x86_64-pc-windows-msvc.tar.gz": "c46402575357a6b88847982f253ed0f6312051ff4ffaacbf089bd23a24756526",
+ "dist/2023-11-16/rust-std-1.74.0-x86_64-pc-windows-msvc.tar.xz": "4a8f665966477248c87b0cff66a7c9a7c3d0e9cb6b9d814df679b26e317f767b",
+ "dist/2023-11-16/rust-std-1.74.0-x86_64-sun-solaris.tar.gz": "8a10b5f7d79d3f002b307a7cd3340f6484d142345f25d79228ed602e0680787f",
+ "dist/2023-11-16/rust-std-1.74.0-x86_64-sun-solaris.tar.xz": "5374872c598365b2d26e29d07873f9a7cb013a111d0166e3ec8ddd17cfc44ee6",
+ "dist/2023-11-16/rust-std-1.74.0-x86_64-unknown-freebsd.tar.gz": "ac3d63c390f27c396b2ad81e331aa7aa9a7e327940e299892e618168de1132c6",
+ "dist/2023-11-16/rust-std-1.74.0-x86_64-unknown-freebsd.tar.xz": "a72ef70989ff1d89d6c593b789367f7f499be7f47b8fbbcef5de43782311a6f5",
+ "dist/2023-11-16/rust-std-1.74.0-x86_64-unknown-fuchsia.tar.gz": "bf1d933b3514edd16a3990f48fb9adaeda190a72fb25c48dd33ea1447e2d38a0",
+ "dist/2023-11-16/rust-std-1.74.0-x86_64-unknown-fuchsia.tar.xz": "00ffd47b9a75fe751e94cc4eac17e84d9b8916b8d79eae42fece1ad153c8572b",
+ "dist/2023-11-16/rust-std-1.74.0-x86_64-unknown-illumos.tar.gz": "c015995c6bf2771161446f952ef4de16d56eeab4edec28b2f775118745d01f64",
+ "dist/2023-11-16/rust-std-1.74.0-x86_64-unknown-illumos.tar.xz": "b2084166f1f6edcdf444851c5e675d708a165b35e5b362b933fb355ae64f9a8a",
+ "dist/2023-11-16/rust-std-1.74.0-x86_64-unknown-linux-gnu.tar.gz": "798b3243d9236e4dc5d43f6b186333cd30c04926b2229568d1fc0f0eb432507f",
+ "dist/2023-11-16/rust-std-1.74.0-x86_64-unknown-linux-gnu.tar.xz": "548413213012e2f62b08ed8a913a51210ae7402619027224580176031f2789ea",
+ "dist/2023-11-16/rust-std-1.74.0-x86_64-unknown-linux-gnux32.tar.gz": "b8248fd0472ff14f2d3e07193439ea5317175342c46a4b138b6aa3a7fe833832",
+ "dist/2023-11-16/rust-std-1.74.0-x86_64-unknown-linux-gnux32.tar.xz": "1b19f309fe8c644ec209a988cd7c6d9e9b4a54ea5f590fbbbcd6a57cccdb237a",
+ "dist/2023-11-16/rust-std-1.74.0-x86_64-unknown-linux-musl.tar.gz": "2bea9c2b70d99051ee01459ae3dd2ecc15447673da1ee9b1d7c9a244d1b0a454",
+ "dist/2023-11-16/rust-std-1.74.0-x86_64-unknown-linux-musl.tar.xz": "b9e825568ab975c792d691675cb43c9f32431178b3e0ecb7ed47fce5e6fedf1a",
+ "dist/2023-11-16/rust-std-1.74.0-x86_64-unknown-netbsd.tar.gz": "12ca22525058af2d3a5f7199345ccd34aa1ac4dd49ea87bc04268cd914c961b6",
+ "dist/2023-11-16/rust-std-1.74.0-x86_64-unknown-netbsd.tar.xz": "f9d80793de095cb1b7eb92dfe7369f436b5399ebcc476ba26e9ff34e487f8793",
+ "dist/2023-11-16/rust-std-1.74.0-x86_64-unknown-none.tar.gz": "4f84dcc141930810bfc2a34ea38008e150199db0989ec39f187dd77d46c14ca7",
+ "dist/2023-11-16/rust-std-1.74.0-x86_64-unknown-none.tar.xz": "c30f849ce57c4764d37070a5f1684a977478c7fbfc63b0f74c37f6d659a3d08e",
+ "dist/2023-11-16/rust-std-1.74.0-x86_64-unknown-redox.tar.gz": "ce5a2d6b7a44182501739f25dd58b99c58a2041915a913a4e6d40aa5eda5e941",
+ "dist/2023-11-16/rust-std-1.74.0-x86_64-unknown-redox.tar.xz": "69d470f879bba73dada576f7c8b027590045908fba1b376f4fc2c543d195808d",
+ "dist/2023-11-16/rust-std-1.74.0-x86_64-unknown-uefi.tar.gz": "48ce0caab15b9bcb1ee8726c5b2ef8c2160e30f1c51b0da01b02692d5b2978d8",
+ "dist/2023-11-16/rust-std-1.74.0-x86_64-unknown-uefi.tar.xz": "f74168a62fdebbc988c7857fd82f5ee5032c6893f84f802817d09a65874b128b",
+ "dist/2023-11-16/rustc-1.74.0-aarch64-apple-darwin.tar.gz": "03a8e33693d4e532368708cff5fafbde71e612c0e5c5f14b5fb80be0d7817a8a",
+ "dist/2023-11-16/rustc-1.74.0-aarch64-apple-darwin.tar.xz": "488a87560a0b162b408e56738751d48e9667e34c43050ec590714f2b6665166c",
+ "dist/2023-11-16/rustc-1.74.0-aarch64-pc-windows-msvc.tar.gz": "216c00eb702d03a32f9314112c90d11b212630b9d6d9a7cf7a04694d6db3f395",
+ "dist/2023-11-16/rustc-1.74.0-aarch64-pc-windows-msvc.tar.xz": "24a9a77afbb42504e531562177af1da9321b4665a3e19dba006f9b26e48f5cb8",
+ "dist/2023-11-16/rustc-1.74.0-aarch64-unknown-linux-gnu.tar.gz": "8e84e8065f21ea01ede5982869dd61160b1999b17f9a79911979ee936aea0de9",
+ "dist/2023-11-16/rustc-1.74.0-aarch64-unknown-linux-gnu.tar.xz": "a49bb365481913ead305658e7e9dc621da7895036b840fb57b1bc85c721d07e6",
+ "dist/2023-11-16/rustc-1.74.0-aarch64-unknown-linux-musl.tar.gz": "8d4c47a70b8b07f1c8906dde2e69efb577843e43acb5b25dc623f572cf2a79c8",
+ "dist/2023-11-16/rustc-1.74.0-aarch64-unknown-linux-musl.tar.xz": "2c0842001520e9300431358532ab37a67cfbc71502b6d5d6e05e8ad0f944338f",
+ "dist/2023-11-16/rustc-1.74.0-arm-unknown-linux-gnueabi.tar.gz": "a05ca6fc4d70499390a609ece2234dd1b9b1a7bde5c0d16384c5fe5cf0969d35",
+ "dist/2023-11-16/rustc-1.74.0-arm-unknown-linux-gnueabi.tar.xz": "db899616c921491822de7d3d63fd830fdea2d05e42ae9f1da39bbf13c8f40455",
+ "dist/2023-11-16/rustc-1.74.0-arm-unknown-linux-gnueabihf.tar.gz": "41e6f864c2be2081ad01b8c8315f00f2b0cfd1f26242f1b6fa887b3f92b4f10d",
+ "dist/2023-11-16/rustc-1.74.0-arm-unknown-linux-gnueabihf.tar.xz": "92001f00d2121cd1e66d84b7467880f65b016ea2ccb5145c0628cc2dbc0bf326",
+ "dist/2023-11-16/rustc-1.74.0-armv7-unknown-linux-gnueabihf.tar.gz": "f55672d155e04c4ecb8187144da3e5ed323629f6f5c766c6841d713565fd8a17",
+ "dist/2023-11-16/rustc-1.74.0-armv7-unknown-linux-gnueabihf.tar.xz": "37c6fb4ab21e195f1393109970001ad9ee15278198cb005a0af766ab33304d01",
+ "dist/2023-11-16/rustc-1.74.0-i686-pc-windows-gnu.tar.gz": "bb4dd9a2e090d8a996e3e91f5276fca7c88f7862bdf4594288c7eff7e981a603",
+ "dist/2023-11-16/rustc-1.74.0-i686-pc-windows-gnu.tar.xz": "367ea77b06dbd1397c3b3b91038fb7e19b592ed71226c2ad95fd901c9af8dd2f",
+ "dist/2023-11-16/rustc-1.74.0-i686-pc-windows-msvc.tar.gz": "107f998a164e357fb396d0244819819510e833eee59953935afd11a9a7c06cb5",
+ "dist/2023-11-16/rustc-1.74.0-i686-pc-windows-msvc.tar.xz": "8b9e90f645a0c49f57cd7e78ed9991dc5b3790231206b8d325167a655899964c",
+ "dist/2023-11-16/rustc-1.74.0-i686-unknown-linux-gnu.tar.gz": "9746152dbd939f2a2e3a52090cd5a1c012c519ca5bd6bba4119250f3c0495efd",
+ "dist/2023-11-16/rustc-1.74.0-i686-unknown-linux-gnu.tar.xz": "7a2bc1bf7e51942d32e82f461eacebe7f929c3eec210dcb7dc6624efd997d7da",
+ "dist/2023-11-16/rustc-1.74.0-loongarch64-unknown-linux-gnu.tar.gz": "73fd7b048f54a257945a1ecefc801359e70747f2384ea3512357ed480f942fd8",
+ "dist/2023-11-16/rustc-1.74.0-loongarch64-unknown-linux-gnu.tar.xz": "703e8c81f9ca3100fc459db92fd5899de62cf77393f334f98159cd97feb11633",
+ "dist/2023-11-16/rustc-1.74.0-powerpc-unknown-linux-gnu.tar.gz": "f6a4050b3da4988a736db1aadc1fea3c81a32a31e92824d27c5afe20e6257a31",
+ "dist/2023-11-16/rustc-1.74.0-powerpc-unknown-linux-gnu.tar.xz": "d4095cbe26ec197274dae9409e68843653e8c08c0b79e8cd74e72d9907e99816",
+ "dist/2023-11-16/rustc-1.74.0-powerpc64-unknown-linux-gnu.tar.gz": "6c78deee01fd73e48ae3ac228f0a806af4ca3ffe89193e7d412e55ded84e1ec0",
+ "dist/2023-11-16/rustc-1.74.0-powerpc64-unknown-linux-gnu.tar.xz": "ca162463db262df9d646687386a1c19f15c8ca9bf1f29eea94f2a8a6d7a6102d",
+ "dist/2023-11-16/rustc-1.74.0-powerpc64le-unknown-linux-gnu.tar.gz": "74da49084874790ccdf0967aea34f90598ffba94a5168a39cfe8378d8aa12959",
+ "dist/2023-11-16/rustc-1.74.0-powerpc64le-unknown-linux-gnu.tar.xz": "8727b1a92e88ac1ce05198ee185dac86553edd7f50b726781c9ab64544b59809",
+ "dist/2023-11-16/rustc-1.74.0-riscv64gc-unknown-linux-gnu.tar.gz": "7583e485235dae9b5cf9798ba2db1c060b12bc97700ba6200f94df6aae1516f1",
+ "dist/2023-11-16/rustc-1.74.0-riscv64gc-unknown-linux-gnu.tar.xz": "f4f27f1c40208b61ea7e61f9edf2de1787aea78a1edb7fe15bceb20de5c7a4a3",
+ "dist/2023-11-16/rustc-1.74.0-s390x-unknown-linux-gnu.tar.gz": "453b3d0df5f02a10a1040f6e9a7753200ab11399f4d56851288f669bedd04f5e",
+ "dist/2023-11-16/rustc-1.74.0-s390x-unknown-linux-gnu.tar.xz": "41eae7788549aec58a6980ae6222d3330a01a37d1e7856d087a4e9c8a19aa890",
+ "dist/2023-11-16/rustc-1.74.0-x86_64-apple-darwin.tar.gz": "3919989c194b2a6674702f0323cac4229d7c6939c933acf4e4451c144f69c8ed",
+ "dist/2023-11-16/rustc-1.74.0-x86_64-apple-darwin.tar.xz": "260d0f51a6880284f4e1471100f37987ba997904cfa8679df6b256c108f38898",
+ "dist/2023-11-16/rustc-1.74.0-x86_64-pc-windows-gnu.tar.gz": "67d254ffc5427916485fdf0d5c56ebed53c8cbd80b08442e7bef976c504267cc",
+ "dist/2023-11-16/rustc-1.74.0-x86_64-pc-windows-gnu.tar.xz": "9f8be832b84bed61c13530126768e6b58d73f847e24eb06f377aea997c403587",
+ "dist/2023-11-16/rustc-1.74.0-x86_64-pc-windows-msvc.tar.gz": "3d790e0413b2efd2f8a6a2f8af7617c582fb8ffb08f5d53dcd5e629d00e04dcb",
+ "dist/2023-11-16/rustc-1.74.0-x86_64-pc-windows-msvc.tar.xz": "21439c238ef18ad2580c167b6505e01274be966ccc02ed75674a2190f204c84c",
+ "dist/2023-11-16/rustc-1.74.0-x86_64-unknown-freebsd.tar.gz": "e131deba63a4b0a79b280737b1eba5d697e43851c18a856422c837d475960eea",
+ "dist/2023-11-16/rustc-1.74.0-x86_64-unknown-freebsd.tar.xz": "34900f7c11fe27b7dbb7c4ca5a93338d39fc7e27439ff7207c792e7369668e36",
+ "dist/2023-11-16/rustc-1.74.0-x86_64-unknown-illumos.tar.gz": "a7b1cef9a95cf104145c5d9316439c5a21a19f10bb885aa8658441b2e60bb1ca",
+ "dist/2023-11-16/rustc-1.74.0-x86_64-unknown-illumos.tar.xz": "8b3ddb2c29600bc50bdde7fc252ca48ce755ec2a42f2326df3c96078eea2eb95",
+ "dist/2023-11-16/rustc-1.74.0-x86_64-unknown-linux-gnu.tar.gz": "358422396f3ff2a073f6fce66ca5aad9ae0596452711f6728c87698846c74e2a",
+ "dist/2023-11-16/rustc-1.74.0-x86_64-unknown-linux-gnu.tar.xz": "7d464be2ae0d6ce69f056d1ea9a8ce2b3b1d537418caea216fdd303903972181",
+ "dist/2023-11-16/rustc-1.74.0-x86_64-unknown-linux-musl.tar.gz": "cf6086ef7ef226b7b0446ad4dd5c008955013b385c6ed34aa35ce37b5815efdf",
+ "dist/2023-11-16/rustc-1.74.0-x86_64-unknown-linux-musl.tar.xz": "ea527c377782513c57ba78717db81e4923e51348bbc098d57bd89b7fdca17ae7",
+ "dist/2023-11-16/rustc-1.74.0-x86_64-unknown-netbsd.tar.gz": "f169216b1f581c584608e21029b3339a5778eb2d37a58148a710149ed163e8d6",
+ "dist/2023-11-16/rustc-1.74.0-x86_64-unknown-netbsd.tar.xz": "71e69d354aa5381fc7db969158e56da0baa55eda792a2e0c034f0f3e4f408f8b"
}
}
diff --git a/src/tools/build-manifest/src/main.rs b/src/tools/build-manifest/src/main.rs
index 5153dd26f..aed6796fa 100644
--- a/src/tools/build-manifest/src/main.rs
+++ b/src/tools/build-manifest/src/main.rs
@@ -102,6 +102,7 @@ static TARGETS: &[&str] = &[
"loongarch64-unknown-none-softfloat",
"m68k-unknown-linux-gnu",
"csky-unknown-linux-gnuabiv2",
+ "csky-unknown-linux-gnuabiv2hf",
"mips-unknown-linux-gnu",
"mips-unknown-linux-musl",
"mips64-unknown-linux-gnuabi64",
@@ -191,7 +192,8 @@ static PKG_INSTALLERS: &[&str] = &["x86_64-apple-darwin", "aarch64-apple-darwin"
static MINGW: &[&str] = &["i686-pc-windows-gnu", "x86_64-pc-windows-gnu"];
-static NIGHTLY_ONLY_COMPONENTS: &[PkgType] = &[PkgType::Miri, PkgType::JsonDocs];
+static NIGHTLY_ONLY_COMPONENTS: &[PkgType] =
+ &[PkgType::Miri, PkgType::JsonDocs, PkgType::RustcCodegenCranelift];
macro_rules! t {
($e:expr) => {
@@ -264,6 +266,29 @@ impl Builder {
// channel-rust-1.XX.toml
let major_minor = rust_version.split('.').take(2).collect::<Vec<_>>().join(".");
self.write_channel_files(&major_minor, &manifest);
+ } else if channel == "beta" {
+ // channel-rust-1.XX.YY-beta.Z.toml
+ let rust_version = self
+ .versions
+ .version(&PkgType::Rust)
+ .expect("missing Rust tarball")
+ .version
+ .expect("missing Rust version")
+ .split(' ')
+ .next()
+ .unwrap()
+ .to_string();
+ self.write_channel_files(&rust_version, &manifest);
+
+ // channel-rust-1.XX.YY-beta.toml
+ let major_minor_patch_beta =
+ rust_version.split('.').take(3).collect::<Vec<_>>().join(".");
+ self.write_channel_files(&major_minor_patch_beta, &manifest);
+
+ // channel-rust-1.XX-beta.toml
+ let major_minor_beta =
+ format!("{}-beta", rust_version.split('.').take(2).collect::<Vec<_>>().join("."));
+ self.write_channel_files(&major_minor_beta, &manifest);
}
if let Some(path) = std::env::var_os("BUILD_MANIFEST_SHIPPED_FILES_PATH") {
@@ -335,7 +360,15 @@ impl Builder {
// NOTE: this profile is effectively deprecated; do not add new components to it.
let mut complete = default;
- complete.extend([Rls, RustAnalyzer, RustSrc, LlvmTools, RustAnalysis, Miri]);
+ complete.extend([
+ Rls,
+ RustAnalyzer,
+ RustSrc,
+ LlvmTools,
+ RustAnalysis,
+ Miri,
+ RustcCodegenCranelift,
+ ]);
profile("complete", &complete);
// The compiler libraries are not stable for end users, and they're also huge, so we only
@@ -422,7 +455,8 @@ impl Builder {
| PkgType::Rustfmt
| PkgType::LlvmTools
| PkgType::RustAnalysis
- | PkgType::JsonDocs => {
+ | PkgType::JsonDocs
+ | PkgType::RustcCodegenCranelift => {
extensions.push(host_component(pkg));
}
PkgType::RustcDev | PkgType::RustcDocs => {
diff --git a/src/tools/build-manifest/src/versions.rs b/src/tools/build-manifest/src/versions.rs
index 7a4c15d01..e4cdf965e 100644
--- a/src/tools/build-manifest/src/versions.rs
+++ b/src/tools/build-manifest/src/versions.rs
@@ -57,6 +57,7 @@ pkg_type! {
LlvmTools = "llvm-tools"; preview = true,
Miri = "miri"; preview = true,
JsonDocs = "rust-docs-json"; preview = true,
+ RustcCodegenCranelift = "rustc-codegen-cranelift"; preview = true,
}
impl PkgType {
@@ -80,6 +81,7 @@ impl PkgType {
PkgType::Rustfmt => false,
PkgType::LlvmTools => false,
PkgType::Miri => false,
+ PkgType::RustcCodegenCranelift => false,
PkgType::Rust => true,
PkgType::RustStd => true,
@@ -106,6 +108,7 @@ impl PkgType {
ReproducibleArtifacts => HOSTS,
RustcDocs => HOSTS,
Cargo => HOSTS,
+ RustcCodegenCranelift => HOSTS,
RustMingw => MINGW,
RustStd => TARGETS,
HtmlDocs => HOSTS,
diff --git a/src/tools/build_helper/src/ci.rs b/src/tools/build_helper/src/ci.rs
index a8505ec95..09489b0d9 100644
--- a/src/tools/build_helper/src/ci.rs
+++ b/src/tools/build_helper/src/ci.rs
@@ -82,15 +82,15 @@ pub mod gha {
fn start_group(name: impl std::fmt::Display) {
if is_in_gha() {
- eprintln!("::group::{name}");
+ println!("::group::{name}");
} else {
- eprintln!("{name}")
+ println!("{name}")
}
}
fn end_group() {
if is_in_gha() {
- eprintln!("::endgroup::");
+ println!("::endgroup::");
}
}
diff --git a/src/tools/build_helper/src/git.rs b/src/tools/build_helper/src/git.rs
index f20b7a2b4..b91dc38e9 100644
--- a/src/tools/build_helper/src/git.rs
+++ b/src/tools/build_helper/src/git.rs
@@ -1,6 +1,11 @@
use std::process::Stdio;
use std::{path::Path, process::Command};
+pub struct GitConfig<'a> {
+ pub git_repository: &'a str,
+ pub nightly_branch: &'a str,
+}
+
/// Runs a command and returns the output
fn output_result(cmd: &mut Command) -> Result<String, String> {
let output = match cmd.stderr(Stdio::inherit()).output() {
@@ -27,7 +32,10 @@ fn output_result(cmd: &mut Command) -> Result<String, String> {
/// upstream https://github.com/rust-lang/rust (fetch)
/// upstream https://github.com/rust-lang/rust (push)
/// ```
-pub fn get_rust_lang_rust_remote(git_dir: Option<&Path>) -> Result<String, String> {
+pub fn get_rust_lang_rust_remote(
+ config: &GitConfig<'_>,
+ git_dir: Option<&Path>,
+) -> Result<String, String> {
let mut git = Command::new("git");
if let Some(git_dir) = git_dir {
git.current_dir(git_dir);
@@ -37,8 +45,8 @@ pub fn get_rust_lang_rust_remote(git_dir: Option<&Path>) -> Result<String, Strin
let rust_lang_remote = stdout
.lines()
- .find(|remote| remote.contains("rust-lang"))
- .ok_or_else(|| "rust-lang/rust remote not found".to_owned())?;
+ .find(|remote| remote.contains(config.git_repository))
+ .ok_or_else(|| format!("{} remote not found", config.git_repository))?;
let remote_name =
rust_lang_remote.split('.').nth(1).ok_or_else(|| "remote name not found".to_owned())?;
@@ -76,9 +84,13 @@ pub fn rev_exists(rev: &str, git_dir: Option<&Path>) -> Result<bool, String> {
/// This could be because the user is updating their forked master branch using the GitHub UI
/// and therefore doesn't need an upstream master branch checked out.
/// We will then fall back to origin/master in the hope that at least this exists.
-pub fn updated_master_branch(git_dir: Option<&Path>) -> Result<String, String> {
- let upstream_remote = get_rust_lang_rust_remote(git_dir)?;
- for upstream_master in [format!("{upstream_remote}/master"), format!("origin/master")] {
+pub fn updated_master_branch(
+ config: &GitConfig<'_>,
+ git_dir: Option<&Path>,
+) -> Result<String, String> {
+ let upstream_remote = get_rust_lang_rust_remote(config, git_dir)?;
+ let branch = config.nightly_branch;
+ for upstream_master in [format!("{upstream_remote}/{branch}"), format!("origin/{branch}")] {
if rev_exists(&upstream_master, git_dir)? {
return Ok(upstream_master);
}
@@ -87,8 +99,11 @@ pub fn updated_master_branch(git_dir: Option<&Path>) -> Result<String, String> {
Err(format!("Cannot find any suitable upstream master branch"))
}
-pub fn get_git_merge_base(git_dir: Option<&Path>) -> Result<String, String> {
- let updated_master = updated_master_branch(git_dir)?;
+pub fn get_git_merge_base(
+ config: &GitConfig<'_>,
+ git_dir: Option<&Path>,
+) -> Result<String, String> {
+ let updated_master = updated_master_branch(config, git_dir)?;
let mut git = Command::new("git");
if let Some(git_dir) = git_dir {
git.current_dir(git_dir);
@@ -100,10 +115,11 @@ pub fn get_git_merge_base(git_dir: Option<&Path>) -> Result<String, String> {
/// The `extensions` parameter can be used to filter the files by their extension.
/// If `extensions` is empty, all files will be returned.
pub fn get_git_modified_files(
+ config: &GitConfig<'_>,
git_dir: Option<&Path>,
extensions: &Vec<&str>,
) -> Result<Option<Vec<String>>, String> {
- let merge_base = get_git_merge_base(git_dir)?;
+ let merge_base = get_git_merge_base(config, git_dir)?;
let mut git = Command::new("git");
if let Some(git_dir) = git_dir {
@@ -122,8 +138,11 @@ pub fn get_git_modified_files(
}
/// Returns the files that haven't been added to git yet.
-pub fn get_git_untracked_files(git_dir: Option<&Path>) -> Result<Option<Vec<String>>, String> {
- let Ok(_updated_master) = updated_master_branch(git_dir) else {
+pub fn get_git_untracked_files(
+ config: &GitConfig<'_>,
+ git_dir: Option<&Path>,
+) -> Result<Option<Vec<String>>, String> {
+ let Ok(_updated_master) = updated_master_branch(config, git_dir) else {
return Ok(None);
};
let mut git = Command::new("git");
diff --git a/src/tools/cargo/.github/renovate.json5 b/src/tools/cargo/.github/renovate.json5
index b633fc245..03e6d8da8 100644
--- a/src/tools/cargo/.github/renovate.json5
+++ b/src/tools/cargo/.github/renovate.json5
@@ -12,29 +12,59 @@
{
customType: 'regex',
fileMatch: [
- '^Cargo.toml$',
+ 'Cargo.toml$',
],
matchStrings: [
- 'rust-version.*?(?<currentValue>\\d+\\.\\d+(\\.\\d+)?)',
+ '\bMSRV:1\b.*?(?<currentValue>\\d+\\.\\d+(\\.\\d+)?)',
+ '(?<currentValue>\\d+\\.\\d+(\\.\\d+)?).*?\bMSRV:1\b',
],
- depNameTemplate: 'latest-msrv',
+ depNameTemplate: 'MSRV:1', // Support 1 version of rustc
+ packageNameTemplate: 'rust-lang/rust',
+ datasourceTemplate: 'github-releases',
+ },
+ {
+ customType: 'regex',
+ fileMatch: [
+ 'Cargo.toml$',
+ ],
+ matchStrings: [
+ '\bMSRV:3\b.*?(?<currentValue>\\d+\\.\\d+(\\.\\d+)?)',
+ '(?<currentValue>\\d+\\.\\d+(\\.\\d+)?).*?\bMSRV:3\b',
+ ],
+ depNameTemplate: 'MSRV:3', // Support 3 versions of rustc
packageNameTemplate: 'rust-lang/rust',
datasourceTemplate: 'github-releases',
},
],
packageRules: [
{
- commitMessageTopic: 'Latest MSRV',
+ commitMessageTopic: 'MSRV (1 version)',
+ matchManagers: [
+ 'regex',
+ ],
+ matchPackageNames: [
+ 'MSRV:1',
+ ],
+ schedule: [
+ '* * * * *',
+ ],
+ groupName: 'msrv',
+ },
+ {
+ commitMessageTopic: 'MSRV (3 versions)',
matchManagers: [
'regex',
],
matchPackageNames: [
- 'latest-msrv',
+ 'MSRV:3',
],
"extractVersion": "^(?<version>\\d+\\.\\d+)", // Drop the patch version
schedule: [
'* * * * *',
],
+ minimumReleaseAge: '85 days', // 2 releases back * 6 weeks per release * 7 days per week + 1
+ internalChecksFilter: 'strict',
+ groupName: 'msrv',
},
// Goals:
// - Rollup safe upgrades to reduce CI runner load
diff --git a/src/tools/cargo/.github/workflows/audit.yml b/src/tools/cargo/.github/workflows/audit.yml
index 14e35b7b3..d903eb0d7 100644
--- a/src/tools/cargo/.github/workflows/audit.yml
+++ b/src/tools/cargo/.github/workflows/audit.yml
@@ -21,7 +21,7 @@ jobs:
- advisories
- bans licenses sources
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- uses: EmbarkStudios/cargo-deny-action@v1
# Prevent sudden announcement of a new advisory from failing ci:
continue-on-error: ${{ matrix.checks == 'advisories' }}
diff --git a/src/tools/cargo/.github/workflows/contrib.yml b/src/tools/cargo/.github/workflows/contrib.yml
index bbd4a7ef7..a4c1cb5d0 100644
--- a/src/tools/cargo/.github/workflows/contrib.yml
+++ b/src/tools/cargo/.github/workflows/contrib.yml
@@ -4,6 +4,10 @@ on:
branches:
- master
+concurrency:
+ cancel-in-progress: false
+ group: "gh-pages"
+
permissions:
contents: read
@@ -13,7 +17,7 @@ jobs:
contents: write # for Git to git push
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Install mdbook
@@ -23,16 +27,26 @@ jobs:
echo `pwd`/mdbook >> $GITHUB_PATH
- name: Deploy docs
run: |
+ GENERATE_PY="$(pwd)/ci/generate.py"
+
cd src/doc/contrib
mdbook build
- git worktree add gh-pages gh-pages
+
+ # Override previous ref to avoid keeping history.
+ git worktree add --orphan -B gh-pages gh-pages
git config user.name "Deploy from CI"
git config user.email ""
cd gh-pages
- # Delete the ref to avoid keeping history.
- git update-ref -d refs/heads/gh-pages
- rm -rf contrib
mv ../book contrib
git add contrib
+
+ # Generate HTML for link redirections.
+ python3 "$GENERATE_PY"
+ git add *.html
+ # WARN: The CNAME file is for GitHub to redirect requests to the custom domain.
+ # Missing this may entail security hazard and domain takeover.
+ # See <https://docs.github.com/en/pages/configuring-a-custom-domain-for-your-github-pages-site/managing-a-custom-domain-for-your-github-pages-site#securing-your-custom-domain>
+ git add CNAME
+
git commit -m "Deploy $GITHUB_SHA to gh-pages"
- git push --force
+ git push origin +gh-pages
diff --git a/src/tools/cargo/.github/workflows/main.yml b/src/tools/cargo/.github/workflows/main.yml
index 44dd76e13..7b8055223 100644
--- a/src/tools/cargo/.github/workflows/main.yml
+++ b/src/tools/cargo/.github/workflows/main.yml
@@ -20,7 +20,7 @@ jobs:
needs:
- build_std
- clippy
- - credential_msrv
+ - msrv
- docs
- lockfile
- resolver
@@ -38,7 +38,7 @@ jobs:
needs:
- build_std
- clippy
- - credential_msrv
+ - msrv
- docs
- lockfile
- resolver
@@ -54,7 +54,7 @@ jobs:
rustfmt:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- run: rustup update stable && rustup default stable
- run: rustup component add rustfmt
- run: cargo fmt --all --check
@@ -63,7 +63,7 @@ jobs:
clippy:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- run: rustup update stable && rustup default stable
- run: rustup component add clippy
# Only check cargo lib for now
@@ -73,7 +73,7 @@ jobs:
stale-label:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- run: rustup update stable && rustup default stable
- run: cargo stale-label
@@ -81,7 +81,7 @@ jobs:
lockfile:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- run: rustup update stable && rustup default stable
- run: cargo update -p cargo --locked
@@ -91,14 +91,14 @@ jobs:
BASE_SHA: ${{ github.event.pull_request.base.sha }}
HEAD_SHA: ${{ github.event.pull_request.head.sha != '' && github.event.pull_request.head.sha || github.sha }}
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
with:
fetch-depth: 0
- run: rustup update stable && rustup default stable
- name: Install cargo-semver-checks
run: |
mkdir installed-bins
- curl -Lf https://github.com/obi1kenobi/cargo-semver-checks/releases/download/v0.22.1/cargo-semver-checks-x86_64-unknown-linux-gnu.tar.gz \
+ curl -Lf https://github.com/obi1kenobi/cargo-semver-checks/releases/download/v0.24.0/cargo-semver-checks-x86_64-unknown-linux-gnu.tar.gz \
| tar -xz --directory=./installed-bins
echo `pwd`/installed-bins >> $GITHUB_PATH
- run: ci/validate-version-bump.sh
@@ -145,7 +145,7 @@ jobs:
other: i686-pc-windows-gnu
name: Tests ${{ matrix.name }}
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- name: Dump Environment
run: ci/dump-environment.sh
- run: rustup update --no-self-update ${{ matrix.rust }} && rustup default ${{ matrix.rust }}
@@ -196,14 +196,14 @@ jobs:
resolver:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- run: rustup update stable && rustup default stable
- run: cargo test -p resolver-tests
test_gitoxide:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- run: rustup update --no-self-update stable && rustup default stable
- run: rustup target add i686-unknown-linux-gnu
- run: sudo apt update -y && sudo apt install gcc-multilib libsecret-1-0 libsecret-1-dev -y
@@ -215,7 +215,7 @@ jobs:
build_std:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- run: rustup update nightly && rustup default nightly
- run: rustup component add rust-src
- run: cargo build
@@ -225,7 +225,7 @@ jobs:
docs:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- run: rustup update nightly && rustup default nightly
- run: rustup update stable
- run: rustup component add rust-docs
@@ -249,9 +249,9 @@ jobs:
curl -sSLO https://raw.githubusercontent.com/rust-lang/rust/master/src/tools/linkchecker/linkcheck.sh
sh linkcheck.sh --all --path ../src/doc cargo
- credential_msrv:
+ msrv:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v3
- - run: rustup update 1.70 && rustup default 1.70
- - run: cargo test -p cargo-credential
+ - uses: actions/checkout@v4
+ - uses: taiki-e/install-action@cargo-hack
+ - run: cargo hack check --all-targets --rust-version --workspace --ignore-private
diff --git a/src/tools/cargo/CHANGELOG.md b/src/tools/cargo/CHANGELOG.md
index f2c9bd0eb..08be017a1 100644
--- a/src/tools/cargo/CHANGELOG.md
+++ b/src/tools/cargo/CHANGELOG.md
@@ -1,16 +1,150 @@
# Changelog
+## Cargo 1.75 (2023-12-28)
+[59596f0f...HEAD](https://github.com/rust-lang/cargo/compare/59596f0f...HEAD)
+
+### Added
+
+### Changed
+
+### Fixed
+
+- Fixed corruption when cargo was killed while writing to files.
+ [#12744](https://github.com/rust-lang/cargo/pull/12744)
+
+### Nightly only
+
+### Documentation
+
+- profile: add missing `strip` info.
+ [#12754](https://github.com/rust-lang/cargo/pull/12754)
+
+### Internal
+
+- Updated to `itertools` 0.11.0.
+ [#12759](https://github.com/rust-lang/cargo/pull/12759)
+- Updated to `cargo_metadata` 0.18.0.
+ [#12758](https://github.com/rust-lang/cargo/pull/12758)
+- Disabled the `custom_target::custom_bin_target` test on windows-gnu.
+ [#12763](https://github.com/rust-lang/cargo/pull/12763)
+
## Cargo 1.74 (2023-11-16)
-[80eca0e5...HEAD](https://github.com/rust-lang/cargo/compare/80eca0e5...HEAD)
+[80eca0e5...rust-1.74.0](https://github.com/rust-lang/cargo/compare/80eca0e5...rust-1.74.0)
### Added
+- 🎉 The `[lints]` table has been stabilized, allowing you to configure reporting levels for rustc and other tool lints in `Cargo.toml`.
+ ([RFC 3389](https://github.com/rust-lang/rfcs/blob/master/text/3389-manifest-lint.md))
+ ([docs](https://doc.rust-lang.org/nightly/cargo/reference/manifest.html#the-lints-section))
+ [#12584](https://github.com/rust-lang/cargo/pull/12584)
+ [#12648](https://github.com/rust-lang/cargo/pull/12648)
+- 🎉 The unstable features `credential-process` and `registry-auth` have been stabilized.
+ These features consolidate the way to authenticate with private registries.
+ ([RFC 2730](https://github.com/rust-lang/rfcs/blob/master/text/2730-cargo-token-from-process.md))
+ ([RFC 3139](https://github.com/rust-lang/rfcs/blob/master/text/3139-cargo-alternative-registry-auth.md))
+ ([docs](https://doc.rust-lang.org/nightly/cargo/reference/registry-authentication.html))
+ [#12590](https://github.com/rust-lang/cargo/pull/12590)
+ [#12622](https://github.com/rust-lang/cargo/pull/12622)
+ [#12623](https://github.com/rust-lang/cargo/pull/12623)
+ [#12626](https://github.com/rust-lang/cargo/pull/12626)
+ [#12641](https://github.com/rust-lang/cargo/pull/12641)
+ [#12644](https://github.com/rust-lang/cargo/pull/12644)
+ [#12649](https://github.com/rust-lang/cargo/pull/12649)
+ [#12671](https://github.com/rust-lang/cargo/pull/12671)
+ [#12709](https://github.com/rust-lang/cargo/pull/12709)
+ Notable changes:
+ - Introducing a new protocol for both external and built-in providers to store and retrieve credentials for registry authentication.
+ - Adding the `auth-required` field in the registry index's `config.json`, enabling authenticated sparse index, crate downloads, and search API.
+ - For using alternative registries with authentication, a credential provider must be configured to avoid unknowingly storing unencrypted credentials on disk.
+ - These settings can be configured in `[registry]` and `[registries]` tables.
+- 🎉 `--keep-going` flag has been stabilized and is now available in each build command
+ (except `bench` and `test`, which have `--no-fail-fast` instead).
+ ([docs](https://doc.rust-lang.org/cargo/commands/cargo-build.html#option-cargo-build---keep-going))
+ [#12568](https://github.com/rust-lang/cargo/pull/12568)
+- Added `--dry-run` flag and summary line at the end for `cargo clean`.
+ [#12638](https://github.com/rust-lang/cargo/pull/12638)
+- Added a short alias `-n` for cli option `--dry-run`.
+ [#12660](https://github.com/rust-lang/cargo/pull/12660)
+- Added support for `target.'cfg(..)'.linker`.
+ [#12535](https://github.com/rust-lang/cargo/pull/12535)
+- Allowed incomplete versions when they are unambiguous for flags like `--package`.
+ [#12591](https://github.com/rust-lang/cargo/pull/12591)
+ [#12614](https://github.com/rust-lang/cargo/pull/12614)
+
### Changed
+- ❗️ Changed how arrays in configuration are merged.
+ The order was unspecified and now follows how other configuration types work for consistency.
+ [summary](https://blog.rust-lang.org/inside-rust/2023/08/24/cargo-config-merging.html)
+ [#12515](https://github.com/rust-lang/cargo/pull/12515)
+- ❗️ cargo-clean: error out if `--doc` is mixed with `-p`.
+ [#12637](https://github.com/rust-lang/cargo/pull/12637)
+- cargo-update: silently deprecate `--aggressive` in favor of the new `--recursive`.
+ [#12544](https://github.com/rust-lang/cargo/pull/12544)
+- cargo-update: `-p/--package` can be used as a positional argument.
+ [#12545](https://github.com/rust-lang/cargo/pull/12545)
+ [#12586](https://github.com/rust-lang/cargo/pull/12586)
+- cargo-install: suggest `--git` when the package name looks like a URL.
+ [#12575](https://github.com/rust-lang/cargo/pull/12575)
+- cargo-add: summarize the feature list when it's too long.
+ [#12662](https://github.com/rust-lang/cargo/pull/12662)
+ [#12702](https://github.com/rust-lang/cargo/pull/12702)
+- Shell completion for `--target` uses rustup but falls back to rustc.
+ [#12606](https://github.com/rust-lang/cargo/pull/12606)
+- Help users know possible `--target` values.
+ [#12607](https://github.com/rust-lang/cargo/pull/12607)
+- Enhanced "registry index not found" error message.
+ [#12732](https://github.com/rust-lang/cargo/pull/12732)
+- Enhanced CLI help message of `--explain`.
+ [#12592](https://github.com/rust-lang/cargo/pull/12592)
+- Enhanced deserialization errors of untagged enums with `serde-untagged`.
+ [#12574](https://github.com/rust-lang/cargo/pull/12574)
+ [#12581](https://github.com/rust-lang/cargo/pull/12581)
+- Enhanced the error when mismatching prerelease version candidates.
+ [#12659](https://github.com/rust-lang/cargo/pull/12659)
+- Enhanced the suggestion on ambiguous Package ID spec.
+ [#12685](https://github.com/rust-lang/cargo/pull/12685)
+- Enhanced TOML parse errors to show the context.
+ [#12556](https://github.com/rust-lang/cargo/pull/12556)
+- Enhanced filesystem error by adding wrappers around `std::fs::metadata`.
+ [#12636](https://github.com/rust-lang/cargo/pull/12636)
+- Enhanced resolver version mismatch warning.
+ [#12573](https://github.com/rust-lang/cargo/pull/12573)
+- Use clap to suggest alternative argument for unsupported arguments.
+ [#12529](https://github.com/rust-lang/cargo/pull/12529)
+ [#12693](https://github.com/rust-lang/cargo/pull/12693)
+ [#12723](https://github.com/rust-lang/cargo/pull/12723)
+- Removed redundant information from cargo new/init `--help` output.
+ [#12594](https://github.com/rust-lang/cargo/pull/12594)
+- Console output and styling tweaks.
+ [#12578](https://github.com/rust-lang/cargo/pull/12578)
+ [#12655](https://github.com/rust-lang/cargo/pull/12655)
+ [#12593](https://github.com/rust-lang/cargo/pull/12593)
+
### Fixed
+- Use full target spec for `cargo rustc --print --target`.
+ [#12743](https://github.com/rust-lang/cargo/pull/12743)
+- Copy PDBs also for EFI targets.
+ [#12688](https://github.com/rust-lang/cargo/pull/12688)
+- Fixed resolver behavior being independent of package order.
+ [#12602](https://github.com/rust-lang/cargo/pull/12602)
+- Fixed unnecessary clean up of `profile.release.package."*"` for `cargo remove`.
+ [#12624](https://github.com/rust-lang/cargo/pull/12624)
+
### Nightly only
+- `-Zasymmetric-token`: Created dedicated unstable flag for asymmetric-token support.
+ [#12551](https://github.com/rust-lang/cargo/pull/12551)
+- `-Zasymmetric-token`: Improved logout message for asymmetric tokens.
+ [#12587](https://github.com/rust-lang/cargo/pull/12587)
+- `-Zmsrv-policy`: **Very** preliminary MSRV resolver support.
+ [#12560](https://github.com/rust-lang/cargo/pull/12560)
+- `-Zscript`: Hack in code fence support.
+ [#12681](https://github.com/rust-lang/cargo/pull/12681)
+- `-Zbindeps`: Support dependencies from registries.
+ [#12421](https://github.com/rust-lang/cargo/pull/12421)
+
### Documentation
- ❗ Policy change: Checking `Cargo.lock` into version control is now the default choice,
@@ -19,6 +153,83 @@
[Lockfile docs](https://doc.rust-lang.org/nightly/cargo/guide/cargo-toml-vs-cargo-lock.html),
[CI docs](https://doc.rust-lang.org/nightly/cargo/guide/continuous-integration.html),
[#12382](https://github.com/rust-lang/cargo/pull/12382)
+ [#12630](https://github.com/rust-lang/cargo/pull/12630)
+- SemVer: Update documentation about removing optional dependencies.
+ [#12687](https://github.com/rust-lang/cargo/pull/12687)
+- Contrib: Add process for security responses.
+ [#12487](https://github.com/rust-lang/cargo/pull/12487)
+- cargo-publish: warn about upload timeout.
+ [#12733](https://github.com/rust-lang/cargo/pull/12733)
+- mdbook: use *AND* search when having multiple terms.
+ [#12548](https://github.com/rust-lang/cargo/pull/12548)
+- Established publish best practices
+ [#12745](https://github.com/rust-lang/cargo/pull/12745)
+- Clarify caret requirements.
+ [#12679](https://github.com/rust-lang/cargo/pull/12679)
+- Clarify how `version` works for `git` dependencies.
+ [#12270](https://github.com/rust-lang/cargo/pull/12270)
+- Clarify and differentiate defaults for split-debuginfo.
+ [#12680](https://github.com/rust-lang/cargo/pull/12680)
+- Added missing `strip` entries in `dev` and `release` profiles.
+ [#12748](https://github.com/rust-lang/cargo/pull/12748)
+
+### Internal
+
+- Updated to `curl-sys` 0.4.66, which corresponds to curl 8.3.0.
+ [#12718](https://github.com/rust-lang/cargo/pull/12718)
+- Updated to `gitoxide` 0.54.1.
+ [#12731](https://github.com/rust-lang/cargo/pull/12731)
+- Updated to `git2` 0.18.0, which corresponds to libgit2 1.7.1.
+ [#12580](https://github.com/rust-lang/cargo/pull/12580)
+- Updated to `cargo_metadata` 0.17.0.
+ [#12758](https://github.com/rust-lang/cargo/pull/12610)
+- Updated target-arch-aware crates to support mips r6 targets
+ [#12720](https://github.com/rust-lang/cargo/pull/12720)
+- publish.py: Remove obsolete `sleep()` calls.
+ [#12686](https://github.com/rust-lang/cargo/pull/12686)
+- Define `{{command}}` for use in src/doc/man/includes
+ [#12570](https://github.com/rust-lang/cargo/pull/12570)
+- Set tracing target `network` for networking messages.
+ [#12582](https://github.com/rust-lang/cargo/pull/12582)
+- cargo-test-support: Add `with_stdout_unordered`.
+ [#12635](https://github.com/rust-lang/cargo/pull/12635)
+- dep: Switch from `termcolor` to `anstream`.
+ [#12751](https://github.com/rust-lang/cargo/pull/12751)
+- Put `Source` trait under `cargo::sources`.
+ [#12527](https://github.com/rust-lang/cargo/pull/12527)
+- SourceId: merge `name` and `alt_registry_key` into one enum.
+ [#12675](https://github.com/rust-lang/cargo/pull/12675)
+- TomlManifest: fail when package_root is not a directory.
+ [#12722](https://github.com/rust-lang/cargo/pull/12722)
+- util: enhanced doc of `network::retry` doc.
+ [#12583](https://github.com/rust-lang/cargo/pull/12583)
+- refactor: Pull out cargo-add MSRV code for reuse
+ [#12553](https://github.com/rust-lang/cargo/pull/12553)
+- refactor(install): Move value parsing to clap
+ [#12547](https://github.com/rust-lang/cargo/pull/12547)
+- Fixed spurious errors with networking tests.
+ [#12726](https://github.com/rust-lang/cargo/pull/12726)
+- Use a more compact relative-time format for `CARGO_LOG` internal logging.
+ [#12542](https://github.com/rust-lang/cargo/pull/12542)
+- Use newer std API for cleaner code.
+ [#12559](https://github.com/rust-lang/cargo/pull/12559)
+ [#12604](https://github.com/rust-lang/cargo/pull/12604)
+ [#12615](https://github.com/rust-lang/cargo/pull/12615)
+ [#12631](https://github.com/rust-lang/cargo/pull/12631)
+- Buffer console status messages.
+ [#12727](https://github.com/rust-lang/cargo/pull/12727)
+- Use enum to describe index summaries to provide a richer information when summaries are not available for resolution.
+ [#12643](https://github.com/rust-lang/cargo/pull/12643)
+- Use shortest path for resolving the path from the given dependency up to the root.
+ [#12678](https://github.com/rust-lang/cargo/pull/12678)
+- Read/write the encoded `cargo update --precise` in the same place
+ [#12629](https://github.com/rust-lang/cargo/pull/12629)
+- Set MSRV for internal packages.
+ [#12381](https://github.com/rust-lang/cargo/pull/12381)
+- ci: Update Renovate schema
+ [#12741](https://github.com/rust-lang/cargo/pull/12741)
+- ci: Ignore patch version in MSRV
+ [#12716](https://github.com/rust-lang/cargo/pull/12716)
## Cargo 1.73 (2023-10-05)
[45782b6b...rust-1.73.0](https://github.com/rust-lang/cargo/compare/45782b6b...rust-1.73.0)
@@ -32,9 +243,13 @@
### Changed
-- Cargo now bails out when using `cargo::` in custom build scripts. This is
+- ❗️ Cargo now bails out when using `cargo::` in custom build scripts. This is
a preparation for an upcoming change in build script invocations.
[#12332](https://github.com/rust-lang/cargo/pull/12332)
+- ❗️ `cargo login` no longer accept any token after the `--` syntax.
+ Arguments after `--` are now reserved in the preparation of the new credential provider feature.
+ This introduces a regression that overlooks the `cargo login -- <token>` support in preivous versions.
+ [#12499](https://github.com/rust-lang/cargo/pull/12499)
- Make Cargo `--help` easier to browse.
[#11905](https://github.com/rust-lang/cargo/pull/11905)
- Prompt the use of `--nocapture` flag if `cargo test` process is terminated via a signal.
diff --git a/src/tools/cargo/CONTRIBUTING.md b/src/tools/cargo/CONTRIBUTING.md
index 88ffbd3d0..6046d5a35 100644
--- a/src/tools/cargo/CONTRIBUTING.md
+++ b/src/tools/cargo/CONTRIBUTING.md
@@ -8,14 +8,12 @@ Contributing documentation has moved to the **[Cargo Contributor Guide]**.
We encourage people to discuss their design before hacking on code. Typically,
you [file an issue] or start a thread on the [internals forum] before submitting
-a pull request. Please read [the process] of how features and bugs are managed
-in Cargo.
+a pull request.
-**NOTICE: Due to limited review capacity, the Cargo team is not accepting new
-features or major changes at this time. Please consult with the team before
-opening a new PR. Only issues that have been explicitly marked as accepted
-will be reviewed.**
+Please read [the process] of how features and bugs are managed in Cargo.
+**Only issues that have been explicitly marked as [accepted] will be reviewed.**
[internals forum]: https://internals.rust-lang.org/c/tools-and-infrastructure/cargo
[file an issue]: https://github.com/rust-lang/cargo/issues
[the process]: https://doc.crates.io/contrib/process/index.html
+[accepted]: https://github.com/rust-lang/cargo/issues?q=is%3Aissue+is%3Aopen+label%3AS-accepted
diff --git a/src/tools/cargo/Cargo.lock b/src/tools/cargo/Cargo.lock
index cc0cb9a88..a2d339b0c 100644
--- a/src/tools/cargo/Cargo.lock
+++ b/src/tools/cargo/Cargo.lock
@@ -25,9 +25,9 @@ checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299"
[[package]]
name = "anstream"
-version = "0.6.3"
+version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "83d7b3983a025adeb201ef26a5564ebd1641ea9851f6282aee4940f745a3c07c"
+checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44"
dependencies = [
"anstyle",
"anstyle-parse",
@@ -97,9 +97,9 @@ checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf"
[[package]]
name = "base64"
-version = "0.21.3"
+version = "0.21.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "414dcefbc63d77c526a76b3afcf6fbb9b5e2791c19c3aa2297733208750c6e53"
+checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9"
[[package]]
name = "base64ct"
@@ -141,9 +141,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitflags"
-version = "2.3.3"
+version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "630be753d4e58660abd17930c71b647fe46c27ea6b63cc59e1e3851406972e42"
+checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635"
[[package]]
name = "bitmaps"
@@ -190,12 +190,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1"
[[package]]
-name = "byteorder"
-version = "1.4.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
-
-[[package]]
name = "bytes"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -208,12 +202,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3e368af43e418a04d52505cf3dbc23dda4e3407ae2fa99fd0e4f308ce546acc"
[[package]]
-name = "byteyarn"
-version = "0.2.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a7534301c0ea17abb4db06d75efc7b4b0fa360fce8e175a4330d721c71c942ff"
-
-[[package]]
name = "camino"
version = "1.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -234,7 +222,7 @@ dependencies = [
[[package]]
name = "cargo"
-version = "0.75.0"
+version = "0.76.0"
dependencies = [
"anstream",
"anstyle",
@@ -245,7 +233,7 @@ dependencies = [
"cargo-credential-libsecret",
"cargo-credential-macos-keychain",
"cargo-credential-wincred",
- "cargo-platform 0.1.5",
+ "cargo-platform 0.1.6",
"cargo-test-macro",
"cargo-test-support",
"cargo-util",
@@ -259,7 +247,7 @@ dependencies = [
"git2",
"git2-curl",
"gix",
- "gix-features",
+ "gix-features 0.35.0",
"glob",
"hex",
"hmac",
@@ -269,7 +257,7 @@ dependencies = [
"ignore",
"im-rc",
"indexmap",
- "itertools",
+ "itertools 0.11.0",
"jobserver",
"lazycell",
"libc",
@@ -293,7 +281,8 @@ dependencies = [
"sha1",
"shell-escape",
"snapbox",
- "syn 2.0.29",
+ "supports-hyperlinks",
+ "syn 2.0.38",
"tar",
"tempfile",
"time",
@@ -311,7 +300,7 @@ dependencies = [
[[package]]
name = "cargo-credential"
-version = "0.4.0"
+version = "0.4.1"
dependencies = [
"anyhow",
"libc",
@@ -325,7 +314,7 @@ dependencies = [
[[package]]
name = "cargo-credential-1password"
-version = "0.4.0"
+version = "0.4.2"
dependencies = [
"cargo-credential",
"serde",
@@ -334,7 +323,7 @@ dependencies = [
[[package]]
name = "cargo-credential-libsecret"
-version = "0.3.2"
+version = "0.4.1"
dependencies = [
"anyhow",
"cargo-credential",
@@ -343,7 +332,7 @@ dependencies = [
[[package]]
name = "cargo-credential-macos-keychain"
-version = "0.3.1"
+version = "0.4.1"
dependencies = [
"cargo-credential",
"security-framework",
@@ -351,7 +340,7 @@ dependencies = [
[[package]]
name = "cargo-credential-wincred"
-version = "0.3.1"
+version = "0.4.1"
dependencies = [
"cargo-credential",
"windows-sys",
@@ -368,7 +357,7 @@ dependencies = [
[[package]]
name = "cargo-platform"
-version = "0.1.5"
+version = "0.1.6"
dependencies = [
"serde",
]
@@ -391,7 +380,7 @@ dependencies = [
"flate2",
"git2",
"glob",
- "itertools",
+ "itertools 0.11.0",
"pasetors",
"serde",
"serde_json",
@@ -405,7 +394,7 @@ dependencies = [
[[package]]
name = "cargo-util"
-version = "0.2.7"
+version = "0.2.8"
dependencies = [
"anyhow",
"core-foundation",
@@ -425,9 +414,9 @@ dependencies = [
[[package]]
name = "cargo_metadata"
-version = "0.17.0"
+version = "0.18.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e7daec1a2a2129eeba1644b220b4647ec537b0b5d4bfd6876fcc5a540056b592"
+checksum = "2d886547e41f740c616ae73108f6eb70afe6d940c7bc697cb30f13daec073037"
dependencies = [
"camino",
"cargo-platform 0.1.2",
@@ -487,18 +476,18 @@ dependencies = [
[[package]]
name = "clap"
-version = "4.4.6"
+version = "4.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d04704f56c2cde07f43e8e2c154b43f216dc5c92fc98ada720177362f953b956"
+checksum = "ac495e00dcec98c83465d5ad66c5c4fabd652fd6686e7c6269b117e729a6f17b"
dependencies = [
"clap_builder",
]
[[package]]
name = "clap_builder"
-version = "4.4.6"
+version = "4.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0e231faeaca65ebd1ea3c737966bf858971cd38c3849107aa3ea7de90a804e45"
+checksum = "c77ed9a32a62e6ca27175d00d29d05ca32e396ea1eb5fb01d8256b669cec7663"
dependencies = [
"anstream",
"anstyle",
@@ -509,9 +498,9 @@ dependencies = [
[[package]]
name = "clap_lex"
-version = "0.5.0"
+version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b"
+checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1"
[[package]]
name = "clru"
@@ -521,18 +510,18 @@ checksum = "b8191fa7302e03607ff0e237d4246cc043ff5b3cb9409d995172ba3bea16b807"
[[package]]
name = "color-print"
-version = "0.3.4"
+version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f2a5e6504ed8648554968650feecea00557a3476bc040d0ffc33080e66b646d0"
+checksum = "7a858372ff14bab9b1b30ea504f2a4bc534582aee3e42ba2d41d2a7baba63d5d"
dependencies = [
"color-print-proc-macro",
]
[[package]]
name = "color-print-proc-macro"
-version = "0.3.4"
+version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d51beaa537d73d2d1ff34ee70bc095f170420ab2ec5d687ecd3ec2b0d092514b"
+checksum = "57e37866456a721d0a404439a1adae37a31be4e0055590d053dfe6981e05003f"
dependencies = [
"nom",
"proc-macro2",
@@ -588,7 +577,7 @@ dependencies = [
[[package]]
name = "crates-io"
-version = "0.39.0"
+version = "0.39.1"
dependencies = [
"curl",
"percent-encoding",
@@ -619,7 +608,7 @@ dependencies = [
"clap",
"criterion-plot",
"is-terminal",
- "itertools",
+ "itertools 0.10.5",
"num-traits",
"once_cell",
"oorandom",
@@ -640,7 +629,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1"
dependencies = [
"cast",
- "itertools",
+ "itertools 0.10.5",
]
[[package]]
@@ -935,15 +924,15 @@ checksum = "d4029edd3e734da6fe05b6cd7bd2960760a616bd2ddd0d59a0124746d6272af0"
dependencies = [
"cfg-if",
"libc",
- "redox_syscall",
+ "redox_syscall 0.3.5",
"windows-sys",
]
[[package]]
name = "flate2"
-version = "1.0.27"
+version = "1.0.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c6c98ee8095e9d1dcbf2fcc6d95acccb90d1c81db1e44725c6a984b1dbdfb010"
+checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e"
dependencies = [
"crc32fast",
"libz-sys",
@@ -1006,11 +995,11 @@ dependencies = [
[[package]]
name = "git2"
-version = "0.18.0"
+version = "0.18.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "12ef350ba88a33b4d524b1d1c79096c9ade5ef8c59395df0e60d1e1889414c0e"
+checksum = "fbf97ba92db08df386e10c8ede66a2a0369bd277090afd8710e19e38de9ec0cd"
dependencies = [
- "bitflags 2.3.3",
+ "bitflags 2.4.0",
"libc",
"libgit2-sys",
"log",
@@ -1033,9 +1022,9 @@ dependencies = [
[[package]]
name = "gix"
-version = "0.54.1"
+version = "0.55.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ad6d32e74454459690d57d18ea4ebec1629936e6b130b51d12cb4a81630ac953"
+checksum = "002667cd1ebb789313d0d0afe3d23b2821cf3b0e91605095f0e6d8751f0ceeea"
dependencies = [
"gix-actor",
"gix-attributes",
@@ -1045,7 +1034,7 @@ dependencies = [
"gix-date",
"gix-diff",
"gix-discover",
- "gix-features",
+ "gix-features 0.36.0",
"gix-filter",
"gix-fs",
"gix-glob",
@@ -1087,9 +1076,9 @@ dependencies = [
[[package]]
name = "gix-actor"
-version = "0.27.0"
+version = "0.28.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "08c60e982c5290897122d4e2622447f014a2dadd5a18cb73d50bb91b31645e27"
+checksum = "948a5f9e43559d16faf583694f1c742eb401ce24ce8e6f2238caedea7486433c"
dependencies = [
"bstr",
"btoi",
@@ -1101,16 +1090,16 @@ dependencies = [
[[package]]
name = "gix-attributes"
-version = "0.19.0"
+version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2451665e70709ba4753b623ef97511ee98c4a73816b2c5b5df25678d607ed820"
+checksum = "dca120f0c6562d2d7cae467f2466e576d9f7f189beec2af2e026145107c729e2"
dependencies = [
"bstr",
- "byteyarn",
"gix-glob",
"gix-path",
"gix-quote",
"gix-trace",
+ "kstring",
"smallvec",
"thiserror",
"unicode-bom",
@@ -1136,22 +1125,22 @@ dependencies = [
[[package]]
name = "gix-command"
-version = "0.2.9"
+version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0f28f654184b5f725c5737c7e4f466cbd8f0102ac352d5257eeab19647ee4256"
+checksum = "3c576cfbf577f72c097b5f88aedea502cd62952bdc1fb3adcab4531d5525a4c7"
dependencies = [
"bstr",
]
[[package]]
name = "gix-commitgraph"
-version = "0.21.0"
+version = "0.22.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e75a975ee22cf0a002bfe9b5d5cb3d2a88e263a8a178cd7509133cff10f4df8a"
+checksum = "7e8bc78b1a6328fa6d8b3a53b6c73997af37fd6bfc1d6c49f149e63bda5cbb36"
dependencies = [
"bstr",
"gix-chunk",
- "gix-features",
+ "gix-features 0.36.0",
"gix-hash",
"memmap2",
"thiserror",
@@ -1159,13 +1148,13 @@ dependencies = [
[[package]]
name = "gix-config"
-version = "0.30.0"
+version = "0.31.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c171514b40487d3f677ae37efc0f45ac980e3169f23c27eb30a70b47fdf88ab5"
+checksum = "5cae98c6b4c66c09379bc35274b172587d6b0ac369a416c39128ad8c6454f9bb"
dependencies = [
"bstr",
"gix-config-value",
- "gix-features",
+ "gix-features 0.36.0",
"gix-glob",
"gix-path",
"gix-ref",
@@ -1184,7 +1173,7 @@ version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea7505b97f4d8e7933e29735a568ba2f86d8de466669d9f0e8321384f9972f47"
dependencies = [
- "bitflags 2.3.3",
+ "bitflags 2.4.0",
"bstr",
"gix-path",
"libc",
@@ -1193,9 +1182,9 @@ dependencies = [
[[package]]
name = "gix-credentials"
-version = "0.20.0"
+version = "0.21.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "46900b884cc5af6a6c141ee741607c0c651a4e1d33614b8d888a1ba81cc0bc8a"
+checksum = "1c5c5d74069b842a1861e581027ac6b7ad9ff66f5911c89b9f45484d7ebda6a4"
dependencies = [
"bstr",
"gix-command",
@@ -1221,9 +1210,9 @@ dependencies = [
[[package]]
name = "gix-diff"
-version = "0.36.0"
+version = "0.37.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "788ddb152c388206e81f36bcbb574e7ed7827c27d8fa62227b34edc333d8928c"
+checksum = "931394f69fb8c9ed6afc0aae3487bd869e936339bcc13ed8884472af072e0554"
dependencies = [
"gix-hash",
"gix-object",
@@ -1232,9 +1221,9 @@ dependencies = [
[[package]]
name = "gix-discover"
-version = "0.25.0"
+version = "0.26.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "69507643d75a0ea9a402fcf73ced517d2b95cc95385904ac09d03e0b952fde33"
+checksum = "a45d5cf0321178883e38705ab2b098f625d609a7d4c391b33ac952eff2c490f2"
dependencies = [
"bstr",
"dunce",
@@ -1251,15 +1240,26 @@ version = "0.35.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b9ff423ae4983f762659040d13dd7a5defbd54b6a04ac3cc7347741cec828cd"
dependencies = [
+ "crossbeam-channel",
+ "gix-hash",
+ "gix-trace",
+ "libc",
+ "parking_lot",
+]
+
+[[package]]
+name = "gix-features"
+version = "0.36.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "51f4365ba17c4f218d7fd9ec102b8d2d3cb0ca200a835e81151ace7778aec827"
+dependencies = [
"bytes",
"crc32fast",
- "crossbeam-channel",
"flate2",
"gix-hash",
"gix-trace",
"libc",
"once_cell",
- "parking_lot",
"prodash",
"sha1_smol",
"thiserror",
@@ -1268,9 +1268,9 @@ dependencies = [
[[package]]
name = "gix-filter"
-version = "0.5.0"
+version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1be40d28cd41445bb6cd52c4d847d915900e5466f7433eaee6a9e0a3d1d88b08"
+checksum = "92f674d3fdb6b1987b04521ec9a5b7be8650671f2c4bbd17c3c81e2a364242ff"
dependencies = [
"bstr",
"encoding_rs",
@@ -1288,30 +1288,30 @@ dependencies = [
[[package]]
name = "gix-fs"
-version = "0.7.0"
+version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "09815faba62fe9b32d918b75a554686c98e43f7d48c43a80df58eb718e5c6635"
+checksum = "8cd171c0cae97cd0dc57e7b4601cb1ebf596450e263ef3c02be9107272c877bd"
dependencies = [
- "gix-features",
+ "gix-features 0.36.0",
]
[[package]]
name = "gix-glob"
-version = "0.13.0"
+version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a9d76e85f11251dcf751d2c5e918a14f562db5be6f727fd24775245653e9b19d"
+checksum = "8fac08925dbc14d414bd02eb45ffb4cecd912d1fce3883f867bd0103c192d3e4"
dependencies = [
- "bitflags 2.3.3",
+ "bitflags 2.4.0",
"bstr",
- "gix-features",
+ "gix-features 0.36.0",
"gix-path",
]
[[package]]
name = "gix-hash"
-version = "0.13.0"
+version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2ccf425543779cddaa4a7c62aba3fa9d90ea135b160be0a72dd93c063121ad4a"
+checksum = "1884c7b41ea0875217c1be9ce91322f90bde433e91d374d0e1276073a51ccc60"
dependencies = [
"faster-hex",
"thiserror",
@@ -1330,9 +1330,9 @@ dependencies = [
[[package]]
name = "gix-ignore"
-version = "0.8.0"
+version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b048f443a1f6b02da4205c34d2e287e3fd45d75e8e2f06cfb216630ea9bff5e3"
+checksum = "1e73c07763a8005ae02cb5cf83040729cea9bb70c7cef68ec6c24159904c499a"
dependencies = [
"bstr",
"gix-glob",
@@ -1342,16 +1342,16 @@ dependencies = [
[[package]]
name = "gix-index"
-version = "0.25.0"
+version = "0.26.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f54d63a9d13c13088f41f5a3accbec284e492ac8f4f707fcc307c139622e17b7"
+checksum = "c83a4fcc121b2f2e109088f677f89f85e7a8ebf39e8e6659c0ae54d4283b1650"
dependencies = [
- "bitflags 2.3.3",
+ "bitflags 2.4.0",
"bstr",
"btoi",
"filetime",
"gix-bitmap",
- "gix-features",
+ "gix-features 0.36.0",
"gix-fs",
"gix-hash",
"gix-lock",
@@ -1365,9 +1365,9 @@ dependencies = [
[[package]]
name = "gix-lock"
-version = "10.0.0"
+version = "11.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "47fc96fa8b6b6d33555021907c81eb3b27635daecf6e630630bdad44f8feaa95"
+checksum = "f4feb1dcd304fe384ddc22edba9dd56a42b0800032de6537728cea2f033a4f37"
dependencies = [
"gix-tempfile",
"gix-utils",
@@ -1382,16 +1382,16 @@ checksum = "9d8acb5ee668d55f0f2d19a320a3f9ef67a6999ad483e11135abcc2464ed18b6"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.29",
+ "syn 2.0.38",
]
[[package]]
name = "gix-negotiate"
-version = "0.8.0"
+version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6f1697bf9911c6d1b8d709b9e6ef718cb5ea5821a1b7991520125a8134448004"
+checksum = "2a5cdcf491ecc9ce39dcc227216c540355fe0024ae7c38e94557752ca5ebb67f"
dependencies = [
- "bitflags 2.3.3",
+ "bitflags 2.4.0",
"gix-commitgraph",
"gix-date",
"gix-hash",
@@ -1403,15 +1403,15 @@ dependencies = [
[[package]]
name = "gix-object"
-version = "0.37.0"
+version = "0.38.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e7e19616c67967374137bae83e950e9b518a9ea8a605069bd6716ada357fd6f"
+checksum = "740f2a44267f58770a1cb3a3d01d14e67b089c7136c48d4bddbb3cfd2bf86a51"
dependencies = [
"bstr",
"btoi",
"gix-actor",
"gix-date",
- "gix-features",
+ "gix-features 0.36.0",
"gix-hash",
"gix-validate",
"itoa 1.0.6",
@@ -1422,13 +1422,13 @@ dependencies = [
[[package]]
name = "gix-odb"
-version = "0.53.0"
+version = "0.54.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8d6a392c6ba3a2f133cdc63120e9bc7aec81eef763db372c817de31febfe64bf"
+checksum = "8630b56cb80d8fa684d383dad006a66401ee8314e12fbf0e566ddad8c115143b"
dependencies = [
"arc-swap",
"gix-date",
- "gix-features",
+ "gix-features 0.36.0",
"gix-hash",
"gix-object",
"gix-pack",
@@ -1441,13 +1441,13 @@ dependencies = [
[[package]]
name = "gix-pack"
-version = "0.43.0"
+version = "0.44.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7536203a45b31e1bc5694bbf90ba8da1b736c77040dd6a520db369f371eb1ab3"
+checksum = "1431ba2e30deff1405920693d54ab231c88d7c240dd6ccc936ee223d8f8697c3"
dependencies = [
"clru",
"gix-chunk",
- "gix-features",
+ "gix-features 0.36.0",
"gix-hash",
"gix-hashtable",
"gix-object",
@@ -1461,9 +1461,9 @@ dependencies = [
[[package]]
name = "gix-packetline"
-version = "0.16.6"
+version = "0.16.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d6df0b75361353e7c0a6d72d49617a37379a7a22cba4569ae33a7720a4c8755a"
+checksum = "8a8384b1e964151aff0d5632dd9b191059d07dff358b96bd940f1b452600d7ab"
dependencies = [
"bstr",
"faster-hex",
@@ -1496,11 +1496,11 @@ dependencies = [
[[package]]
name = "gix-pathspec"
-version = "0.3.0"
+version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c3e26c9b47c51be73f98d38c84494bd5fb99334c5d6fda14ef5d036d50a9e5fd"
+checksum = "e9cc7194fdcf43b4a1ccfa13ffae1d79f83beb4becff7761d88dd99faeafe625"
dependencies = [
- "bitflags 2.3.3",
+ "bitflags 2.4.0",
"bstr",
"gix-attributes",
"gix-config-value",
@@ -1524,15 +1524,15 @@ dependencies = [
[[package]]
name = "gix-protocol"
-version = "0.40.0"
+version = "0.41.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cc7b700dc20cc9be8a5130a1fd7e10c34117ffa7068431c8c24d963f0a2e0c9b"
+checksum = "391e3feabdfa5f90dad6673ce59e3291ac28901b2ff248d86c5a7fbde0391e0e"
dependencies = [
"bstr",
"btoi",
"gix-credentials",
"gix-date",
- "gix-features",
+ "gix-features 0.36.0",
"gix-hash",
"gix-transport",
"maybe-async",
@@ -1553,13 +1553,13 @@ dependencies = [
[[package]]
name = "gix-ref"
-version = "0.37.0"
+version = "0.38.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "22e6b749660b613641769edc1954132eb8071a13c32224891686091bef078de4"
+checksum = "0ec2f6d07ac88d2fb8007ee3fa3e801856fb9d82e7366ec0ca332eb2c9d74a52"
dependencies = [
"gix-actor",
"gix-date",
- "gix-features",
+ "gix-features 0.36.0",
"gix-fs",
"gix-hash",
"gix-lock",
@@ -1574,9 +1574,9 @@ dependencies = [
[[package]]
name = "gix-refspec"
-version = "0.18.0"
+version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0895cb7b1e70f3c3bd4550c329e9f5caf2975f97fcd4238e05754e72208ef61e"
+checksum = "ccb0974cc41dbdb43a180c7f67aa481e1c1e160fcfa8f4a55291fd1126c1a6e7"
dependencies = [
"bstr",
"gix-hash",
@@ -1588,9 +1588,9 @@ dependencies = [
[[package]]
name = "gix-revision"
-version = "0.22.0"
+version = "0.23.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c8c4b15cf2ab7a35f5bcb3ef146187c8d36df0177e171ca061913cbaaa890e89"
+checksum = "2ca97ac73459a7f3766aa4a5638a6e37d56d4c7962bc1986fbaf4883d0772588"
dependencies = [
"bstr",
"gix-date",
@@ -1604,9 +1604,9 @@ dependencies = [
[[package]]
name = "gix-revwalk"
-version = "0.8.0"
+version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e9870c6b1032f2084567710c3b2106ac603377f8d25766b8a6b7c33e6e3ca279"
+checksum = "a16d8c892e4cd676d86f0265bf9d40cefd73d8d94f86b213b8b77d50e77efae0"
dependencies = [
"gix-commitgraph",
"gix-date",
@@ -1623,7 +1623,7 @@ version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92b9542ac025a8c02ed5d17b3fc031a111a384e859d0be3532ec4d58c40a0f28"
dependencies = [
- "bitflags 2.3.3",
+ "bitflags 2.4.0",
"gix-path",
"libc",
"windows",
@@ -1631,9 +1631,9 @@ dependencies = [
[[package]]
name = "gix-submodule"
-version = "0.4.0"
+version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dd0150e82e9282d3f2ab2dd57a22f9f6c3447b9d9856e5321ac92d38e3e0e2b7"
+checksum = "bba78c8d12aa24370178453ec3a472ff08dfaa657d116229f57f2c9cd469a1c2"
dependencies = [
"bstr",
"gix-config",
@@ -1646,9 +1646,9 @@ dependencies = [
[[package]]
name = "gix-tempfile"
-version = "10.0.0"
+version = "11.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5ae0978f3e11dc57290ee75ac2477c815bca1ce2fa7ed5dc5f16db067410ac4d"
+checksum = "05cc2205cf10d99f70b96e04e16c55d4c7cf33efc151df1f793e29fd12a931f8"
dependencies = [
"gix-fs",
"libc",
@@ -1665,16 +1665,16 @@ checksum = "96b6d623a1152c3facb79067d6e2ecdae48130030cf27d6eb21109f13bd7b836"
[[package]]
name = "gix-transport"
-version = "0.37.0"
+version = "0.38.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b9ec726e6a245e68ace59a34126a1d679de60360676612985e70b0d3b102fb4e"
+checksum = "2f209a93364e24f20319751bc11092272e2f3fe82bb72592b2822679cf5be752"
dependencies = [
"base64",
"bstr",
"curl",
"gix-command",
"gix-credentials",
- "gix-features",
+ "gix-features 0.36.0",
"gix-packetline",
"gix-quote",
"gix-sec",
@@ -1684,9 +1684,9 @@ dependencies = [
[[package]]
name = "gix-traverse"
-version = "0.33.0"
+version = "0.34.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "22ef04ab3643acba289b5cedd25d6f53c0430770b1d689d1d654511e6fb81ba0"
+checksum = "14d050ec7d4e1bb76abf0636cf4104fb915b70e54e3ced9a4427c999100ff38a"
dependencies = [
"gix-commitgraph",
"gix-date",
@@ -1700,12 +1700,12 @@ dependencies = [
[[package]]
name = "gix-url"
-version = "0.24.0"
+version = "0.25.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6125ecf46e8c68bf7202da6cad239831daebf0247ffbab30210d72f3856e420f"
+checksum = "b1b9ac8ed32ad45f9fc6c5f8c0be2ed911e544a5a19afd62d95d524ebaa95671"
dependencies = [
"bstr",
- "gix-features",
+ "gix-features 0.36.0",
"gix-path",
"home 0.5.5",
"thiserror",
@@ -1733,13 +1733,13 @@ dependencies = [
[[package]]
name = "gix-worktree"
-version = "0.26.0"
+version = "0.27.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9f5e32972801bd82d56609e6fc84efc358fa1f11f25c5e83b7807ee2280f14fe"
+checksum = "ddaf79e721dba64fe726a42f297a3c8ed42e55cdc0d81ca68452f2def3c2d7fd"
dependencies = [
"bstr",
"gix-attributes",
- "gix-features",
+ "gix-features 0.36.0",
"gix-fs",
"gix-glob",
"gix-hash",
@@ -1856,7 +1856,7 @@ dependencies = [
[[package]]
name = "home"
-version = "0.5.7"
+version = "0.5.8"
dependencies = [
"windows-sys",
]
@@ -1948,6 +1948,15 @@ dependencies = [
]
[[package]]
+name = "itertools"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57"
+dependencies = [
+ "either",
+]
+
+[[package]]
name = "itoa"
version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1961,9 +1970,9 @@ checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6"
[[package]]
name = "jobserver"
-version = "0.1.26"
+version = "0.1.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "936cfd212a0155903bcbc060e316fb6cc7cbf2e1907329391ebadc1fe0ce77c2"
+checksum = "8c37f63953c4c63420ed5fd3d6d398c719489b9f872b9fa683262f8edd363c7d"
dependencies = [
"libc",
]
@@ -1978,6 +1987,15 @@ dependencies = [
]
[[package]]
+name = "kstring"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec3066350882a1cd6d950d055997f379ac37fd39f81cd4d8ed186032eb3c5747"
+dependencies = [
+ "static_assertions",
+]
+
+[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1997,9 +2015,9 @@ checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67"
[[package]]
name = "libc"
-version = "0.2.148"
+version = "0.2.149"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b"
+checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b"
[[package]]
name = "libgit2-sys"
@@ -2017,9 +2035,9 @@ dependencies = [
[[package]]
name = "libloading"
-version = "0.8.0"
+version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d580318f95776505201b28cf98eb1fa5e4be3b689633ba6a3e6cd880ff22d8cb"
+checksum = "c571b676ddfc9a8c12f1f3d3085a7b163966a8fd8098a90640953ce5f6170161"
dependencies = [
"cfg-if",
"windows-sys",
@@ -2069,9 +2087,9 @@ dependencies = [
[[package]]
name = "linux-raw-sys"
-version = "0.4.5"
+version = "0.4.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "57bcfdad1b858c2db7c38303a6d2ad4dfaf5eb53dfeb0910128b2c26d6158503"
+checksum = "da2479e8c062e40bf0066ffa0bc823de0a9368974af99c9f6df941d2c231e03f"
[[package]]
name = "lock_api"
@@ -2124,9 +2142,9 @@ dependencies = [
[[package]]
name = "memchr"
-version = "2.6.2"
+version = "2.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5486aed0026218e61b8a01d5fbd5a0a134649abb71a0e53b7bc088529dced86e"
+checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167"
[[package]]
name = "memmap2"
@@ -2263,7 +2281,7 @@ version = "0.10.57"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bac25ee399abb46215765b1cb35bc0212377e58a061560d8b29b024fd0430e7c"
dependencies = [
- "bitflags 2.3.3",
+ "bitflags 2.4.0",
"cfg-if",
"foreign-types",
"libc",
@@ -2280,7 +2298,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.29",
+ "syn 2.0.38",
]
[[package]]
@@ -2378,7 +2396,7 @@ checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447"
dependencies = [
"cfg-if",
"libc",
- "redox_syscall",
+ "redox_syscall 0.3.5",
"smallvec",
"windows-targets",
]
@@ -2475,7 +2493,7 @@ dependencies = [
"pest_meta",
"proc-macro2",
"quote",
- "syn 2.0.29",
+ "syn 2.0.38",
]
[[package]]
@@ -2566,9 +2584,9 @@ dependencies = [
[[package]]
name = "proc-macro2"
-version = "1.0.66"
+version = "1.0.67"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9"
+checksum = "3d433d9f1a3e8c1263d9456598b16fec66f4acc9a74dacffd35c7bb09b3a1328"
dependencies = [
"unicode-ident",
]
@@ -2584,19 +2602,19 @@ dependencies = [
[[package]]
name = "proptest"
-version = "1.2.0"
+version = "1.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4e35c06b98bf36aba164cc17cb25f7e232f5c4aeea73baa14b8a9f0d92dbfa65"
+checksum = "7c003ac8c77cb07bb74f5f198bce836a689bcd5a42574612bf14d17bfd08c20e"
dependencies = [
"bit-set",
- "bitflags 1.3.2",
- "byteorder",
+ "bit-vec",
+ "bitflags 2.4.0",
"lazy_static",
"num-traits",
"rand",
"rand_chacha",
"rand_xorshift",
- "regex-syntax 0.6.29",
+ "regex-syntax 0.7.2",
"rusty-fork",
"tempfile",
"unarray",
@@ -2714,6 +2732,15 @@ dependencies = [
]
[[package]]
+name = "redox_syscall"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa"
+dependencies = [
+ "bitflags 1.3.2",
+]
+
+[[package]]
name = "regex"
version = "1.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2791,11 +2818,11 @@ dependencies = [
[[package]]
name = "rustix"
-version = "0.38.6"
+version = "0.38.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1ee020b1716f0a80e2ace9b03441a749e402e86712f15f16fe8a8f75afac732f"
+checksum = "2b426b0506e5d50a7d8dafcf2e81471400deb602392c7dd110815afb4eaf02a3"
dependencies = [
- "bitflags 2.3.3",
+ "bitflags 2.4.0",
"errno",
"libc",
"linux-raw-sys",
@@ -2883,9 +2910,9 @@ dependencies = [
[[package]]
name = "semver"
-version = "1.0.18"
+version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918"
+checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090"
dependencies = [
"serde",
]
@@ -2899,9 +2926,9 @@ dependencies = [
[[package]]
name = "serde"
-version = "1.0.188"
+version = "1.0.190"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e"
+checksum = "91d3c334ca1ee894a2c6f6ad698fe8c435b76d504b13d436f0685d648d6d96f7"
dependencies = [
"serde_derive",
]
@@ -2928,13 +2955,13 @@ dependencies = [
[[package]]
name = "serde_derive"
-version = "1.0.188"
+version = "1.0.190"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2"
+checksum = "67c5609f394e5c2bd7fc51efda478004ea80ef42fee983d5c67a65e34f32c0e3"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.29",
+ "syn 2.0.38",
]
[[package]]
@@ -2948,9 +2975,9 @@ dependencies = [
[[package]]
name = "serde_json"
-version = "1.0.105"
+version = "1.0.108"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "693151e1ac27563d6dbcec9dee9fbd5da8539b20fa14ad3752b2e6d363ace360"
+checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b"
dependencies = [
"itoa 1.0.6",
"ryu",
@@ -2959,18 +2986,18 @@ dependencies = [
[[package]]
name = "serde_spanned"
-version = "0.6.3"
+version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "96426c9936fd7a0124915f9185ea1d20aa9445cc9821142f0a73bc9207a2e186"
+checksum = "12022b835073e5b11e90a14f86838ceb1c8fb0325b72416845c487ac0fa95e80"
dependencies = [
"serde",
]
[[package]]
name = "sha1"
-version = "0.10.5"
+version = "0.10.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3"
+checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba"
dependencies = [
"cfg-if",
"cpufeatures",
@@ -2985,9 +3012,9 @@ checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012"
[[package]]
name = "sha2"
-version = "0.10.7"
+version = "0.10.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8"
+checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
dependencies = [
"cfg-if",
"cpufeatures",
@@ -3043,9 +3070,9 @@ checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0"
[[package]]
name = "snapbox"
-version = "0.4.13"
+version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7b439536a42c43be148b610c7f7f968fb79a457254910a9cb20900da73cd3271"
+checksum = "4b377c0b6e4715c116473d8e40d51e3fa5b0a2297ca9b2a931ba800667b259ed"
dependencies = [
"anstream",
"anstyle",
@@ -3090,6 +3117,12 @@ dependencies = [
]
[[package]]
+name = "static_assertions"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
+
+[[package]]
name = "strsim"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3102,6 +3135,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc"
[[package]]
+name = "supports-hyperlinks"
+version = "2.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f84231692eb0d4d41e4cdd0cabfdd2e6cd9e255e65f80c9aa7c98dd502b4233d"
+dependencies = [
+ "is-terminal",
+]
+
+[[package]]
name = "syn"
version = "1.0.109"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3114,9 +3156,9 @@ dependencies = [
[[package]]
name = "syn"
-version = "2.0.29"
+version = "2.0.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c324c494eba9d92503e6f1ef2e6df781e78f6a7705a0202d9801b198807d518a"
+checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b"
dependencies = [
"proc-macro2",
"quote",
@@ -3147,13 +3189,13 @@ dependencies = [
[[package]]
name = "tempfile"
-version = "3.8.0"
+version = "3.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cb94d2f3cc536af71caac6b6fcebf65860b347e7ce0cc9ebe8f70d3e521054ef"
+checksum = "7ef1adac450ad7f4b3c28589471ade84f25f731a7a0fe30d71dfa9f60fd808e5"
dependencies = [
"cfg-if",
"fastrand",
- "redox_syscall",
+ "redox_syscall 0.4.1",
"rustix",
"windows-sys",
]
@@ -3170,22 +3212,22 @@ dependencies = [
[[package]]
name = "thiserror"
-version = "1.0.47"
+version = "1.0.50"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "97a802ec30afc17eee47b2855fc72e0c4cd62be9b4efe6591edde0ec5bd68d8f"
+checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
-version = "1.0.47"
+version = "1.0.50"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6bb623b56e39ab7dcd4b1b98bb6c8f8d907ed255b18de254088016b27a8ee19b"
+checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.29",
+ "syn 2.0.38",
]
[[package]]
@@ -3255,9 +3297,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "toml"
-version = "0.7.6"
+version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c17e963a819c331dcacd7ab957d80bc2b9a9c1e71c804826d2f283dd65306542"
+checksum = "8ff9e3abce27ee2c9a37f9ad37238c1bdd4e789c84ba37df76aa4d528f5072cc"
dependencies = [
"serde",
"serde_spanned",
@@ -3267,18 +3309,18 @@ dependencies = [
[[package]]
name = "toml_datetime"
-version = "0.6.3"
+version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b"
+checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1"
dependencies = [
"serde",
]
[[package]]
name = "toml_edit"
-version = "0.19.14"
+version = "0.20.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f8123f27e969974a3dfba720fdb560be359f57b44302d280ba72e76a74480e8a"
+checksum = "70f427fce4d84c72b5b732388bf4a9f4531b53f74e2887e3ecb2481f68f66d81"
dependencies = [
"indexmap",
"serde",
@@ -3289,11 +3331,10 @@ dependencies = [
[[package]]
name = "tracing"
-version = "0.1.37"
+version = "0.1.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8"
+checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef"
dependencies = [
- "cfg-if",
"pin-project-lite",
"tracing-attributes",
"tracing-core",
@@ -3301,20 +3342,20 @@ dependencies = [
[[package]]
name = "tracing-attributes"
-version = "0.1.26"
+version = "0.1.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab"
+checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.29",
+ "syn 2.0.38",
]
[[package]]
name = "tracing-core"
-version = "0.1.31"
+version = "0.1.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a"
+checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54"
dependencies = [
"once_cell",
"valuable",
@@ -3405,9 +3446,9 @@ dependencies = [
[[package]]
name = "unicode-width"
-version = "0.1.10"
+version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
+checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85"
[[package]]
name = "unicode-xid"
@@ -3548,9 +3589,9 @@ dependencies = [
[[package]]
name = "walkdir"
-version = "2.3.3"
+version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698"
+checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee"
dependencies = [
"same-file",
"winapi-util",
@@ -3583,7 +3624,7 @@ dependencies = [
"once_cell",
"proc-macro2",
"quote",
- "syn 2.0.29",
+ "syn 2.0.38",
"wasm-bindgen-shared",
]
@@ -3605,7 +3646,7 @@ checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.29",
+ "syn 2.0.38",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
@@ -3754,6 +3795,7 @@ dependencies = [
"cargo-util",
"clap",
"git2",
+ "semver",
"tracing",
"tracing-subscriber",
]
diff --git a/src/tools/cargo/Cargo.toml b/src/tools/cargo/Cargo.toml
index 440304416..3fb36b44e 100644
--- a/src/tools/cargo/Cargo.toml
+++ b/src/tools/cargo/Cargo.toml
@@ -11,38 +11,38 @@ exclude = [
]
[workspace.package]
-rust-version = "1.72.0"
+rust-version = "1.73" # MSRV:1
edition = "2021"
license = "MIT OR Apache-2.0"
[workspace.dependencies]
-anstream = "0.6.3"
+anstream = "0.6.4"
anstyle = "1.0.4"
anyhow = "1.0.75"
-base64 = "0.21.3"
+base64 = "0.21.5"
bytesize = "1.3"
cargo = { path = "" }
-cargo-credential = { version = "0.4.0", path = "credential/cargo-credential" }
-cargo-credential-libsecret = { version = "0.3.1", path = "credential/cargo-credential-libsecret" }
-cargo-credential-wincred = { version = "0.3.0", path = "credential/cargo-credential-wincred" }
-cargo-credential-macos-keychain = { version = "0.3.0", path = "credential/cargo-credential-macos-keychain" }
+cargo-credential = { version = "0.4.1", path = "credential/cargo-credential" }
+cargo-credential-libsecret = { version = "0.4.1", path = "credential/cargo-credential-libsecret" }
+cargo-credential-macos-keychain = { version = "0.4.1", path = "credential/cargo-credential-macos-keychain" }
+cargo-credential-wincred = { version = "0.4.1", path = "credential/cargo-credential-wincred" }
cargo-platform = { path = "crates/cargo-platform", version = "0.1.4" }
cargo-test-macro = { path = "crates/cargo-test-macro" }
cargo-test-support = { path = "crates/cargo-test-support" }
cargo-util = { version = "0.2.6", path = "crates/cargo-util" }
-cargo_metadata = "0.17.0"
-clap = "4.4.6"
-color-print = "0.3.4"
+cargo_metadata = "0.18.1"
+clap = "4.4.7"
+color-print = "0.3.5"
core-foundation = { version = "0.9.3", features = ["mac_os_10_7_support"] }
crates-io = { version = "0.39.0", path = "crates/crates-io" }
criterion = { version = "0.5.1", features = ["html_reports"] }
curl = "0.4.44"
curl-sys = "0.4.68"
filetime = "0.2.22"
-flate2 = { version = "1.0.27", default-features = false, features = ["zlib"] }
-git2 = "0.18.0"
+flate2 = { version = "1.0.28", default-features = false, features = ["zlib"] }
+git2 = "0.18.1"
git2-curl = "0.19.0"
-gix = { version = "0.54.1", default-features = false, features = ["blocking-http-transport-curl", "progress-tree", "revision"] }
+gix = { version = "0.55.2", default-features = false, features = ["blocking-http-transport-curl", "progress-tree", "revision"] }
gix-features-for-configuration-only = { version = "0.35.0", package = "gix-features", features = [ "parallel" ] }
glob = "0.3.1"
handlebars = { version = "3.5.5", features = ["dir_source"] }
@@ -54,13 +54,13 @@ humantime = "2.1.0"
ignore = "0.4.20"
im-rc = "15.1.0"
indexmap = "2"
-itertools = "0.10.0"
-jobserver = "0.1.26"
+itertools = "0.11.0"
+jobserver = "0.1.27"
lazycell = "1.3.0"
-libc = "0.2.148"
+libc = "0.2.149"
libgit2-sys = "0.16.1"
-libloading = "0.8.0"
-memchr = "2.6.2"
+libloading = "0.8.1"
+memchr = "2.6.4"
miow = "0.6.0"
opener = "0.6.1"
openssl ="0.10.57"
@@ -70,44 +70,46 @@ pathdiff = "0.2"
percent-encoding = "2.3"
pkg-config = "0.3.27"
pretty_assertions = "1.4.0"
-proptest = "1.2.0"
+proptest = "1.3.1"
pulldown-cmark = { version = "0.9.3", default-features = false }
rand = "0.8.5"
rustfix = "0.6.1"
same-file = "1.0.6"
security-framework = "2.9.2"
-semver = { version = "1.0.18", features = ["serde"] }
-serde = "1.0.188"
+semver = { version = "1.0.20", features = ["serde"] }
+serde = "1.0.190"
serde-untagged = "0.1.1"
serde-value = "0.7.0"
serde_ignored = "0.1.9"
-serde_json = "1.0.105"
-sha1 = "0.10.5"
-sha2 = "0.10.7"
+serde_json = "1.0.108"
+sha1 = "0.10.6"
+sha2 = "0.10.8"
shell-escape = "0.1.5"
-snapbox = { version = "0.4.13", features = ["diff", "path"] }
-syn = { version = "2.0.29", features = ["extra-traits", "full"] }
+supports-hyperlinks = "2.1.0"
+snapbox = { version = "0.4.14", features = ["diff", "path"] }
+syn = { version = "2.0.38", features = ["extra-traits", "full"] }
tar = { version = "0.4.40", default-features = false }
-tempfile = "3.8.0"
-thiserror = "1.0.47"
+tempfile = "3.8.1"
+thiserror = "1.0.50"
time = { version = "0.3", features = ["parsing", "formatting", "serde"] }
-toml = "0.7.6"
-toml_edit = "0.19.14"
-tracing = "0.1.37"
+toml = "0.8.6"
+toml_edit = { version = "0.20.7", features = ["serde"] }
+tracing = "0.1.40"
tracing-subscriber = { version = "0.3.17", features = ["env-filter"] }
unicase = "2.7.0"
-unicode-width = "0.1.10"
+unicode-width = "0.1.11"
unicode-xid = "0.2.4"
url = "2.4.1"
varisat = "0.2.2"
-walkdir = "2.3.3"
+walkdir = "2.4.0"
windows-sys = "0.48"
[package]
name = "cargo"
-version = "0.75.0"
+version = "0.76.0"
edition.workspace = true
license.workspace = true
+rust-version.workspace = true
homepage = "https://crates.io"
repository = "https://github.com/rust-lang/cargo"
documentation = "https://docs.rs/cargo"
@@ -125,14 +127,11 @@ anstyle.workspace = true
anyhow.workspace = true
base64.workspace = true
bytesize.workspace = true
-cargo-platform.workspace = true
cargo-credential.workspace = true
-cargo-credential-libsecret.workspace = true
-cargo-credential-macos-keychain.workspace = true
-cargo-credential-wincred.workspace = true
+cargo-platform.workspace = true
cargo-util.workspace = true
-color-print.workspace = true
clap = { workspace = true, features = ["wrap_help"] }
+color-print.workspace = true
crates-io.workspace = true
curl = { workspace = true, features = ["http2"] }
curl-sys.workspace = true
@@ -172,6 +171,7 @@ serde_ignored.workspace = true
serde_json = { workspace = true, features = ["raw_value"] }
sha1.workspace = true
shell-escape.workspace = true
+supports-hyperlinks.workspace = true
syn.workspace = true
tar.workspace = true
tempfile.workspace = true
@@ -186,9 +186,18 @@ unicode-xid.workspace = true
url.workspace = true
walkdir.workspace = true
+[target.'cfg(target_os = "linux")'.dependencies]
+cargo-credential-libsecret.workspace = true
+
+[target.'cfg(target_os = "macos")'.dependencies]
+cargo-credential-macos-keychain.workspace = true
+
[target.'cfg(not(windows))'.dependencies]
openssl = { workspace = true, optional = true }
+[target.'cfg(windows)'.dependencies]
+cargo-credential-wincred.workspace = true
+
[target.'cfg(windows)'.dependencies.windows-sys]
workspace = true
features = [
diff --git a/src/tools/cargo/ci/generate.py b/src/tools/cargo/ci/generate.py
new file mode 100644
index 000000000..b750729dc
--- /dev/null
+++ b/src/tools/cargo/ci/generate.py
@@ -0,0 +1,49 @@
+#!/usr/bin/env python3
+
+MAPPING = {
+ "build-script.html": "https://doc.rust-lang.org/cargo/reference/build-scripts.html",
+ "config.html": None,
+ "crates-io.html": "https://doc.rust-lang.org/cargo/reference/publishing.html",
+ "environment-variables.html": None,
+ "external-tools.html": None,
+ "faq.html": "https://doc.rust-lang.org/cargo/faq.html",
+ "guide.html": "https://doc.rust-lang.org/cargo/guide/",
+ "index.html": "https://doc.rust-lang.org/cargo/",
+ "manifest.html": None,
+ "pkgid-spec.html": None,
+ "policies.html": "https://crates.io/policies",
+ "source-replacement.html": None,
+ "specifying-dependencies.html": None,
+}
+
+TEMPLATE = """\
+<html>
+<head>
+<meta http-equiv="refresh" content="0; url={mapped}" />
+<script>
+window.location.replace("{mapped}" + window.location.hash);
+</script>
+<title>Page Moved</title>
+</head>
+<body>
+This page has moved. Click <a href="{mapped}">here</a> to go to the new page.
+</body>
+</html>
+"""
+
+def main():
+ for name in sorted(MAPPING):
+ with open(name, 'w') as f:
+ mapped = MAPPING[name]
+ if mapped is None:
+ mapped = "https://doc.rust-lang.org/cargo/reference/{}".format(name)
+ f.write(TEMPLATE.format(name=name, mapped=mapped))
+
+ # WARN: The CNAME file is for GitHub to redirect requests to the custom domain.
+ # Missing this may entail security hazard and domain takeover.
+ # See <https://docs.github.com/en/pages/configuring-a-custom-domain-for-your-github-pages-site/managing-a-custom-domain-for-your-github-pages-site#securing-your-custom-domain>
+ with open('CNAME', 'w') as f:
+ f.write('doc.crates.io')
+
+if __name__ == '__main__':
+ main()
diff --git a/src/tools/cargo/crates/cargo-platform/Cargo.toml b/src/tools/cargo/crates/cargo-platform/Cargo.toml
index 016ead686..786948ff3 100644
--- a/src/tools/cargo/crates/cargo-platform/Cargo.toml
+++ b/src/tools/cargo/crates/cargo-platform/Cargo.toml
@@ -1,8 +1,9 @@
[package]
name = "cargo-platform"
-version = "0.1.5"
+version = "0.1.6"
edition.workspace = true
license.workspace = true
+rust-version = "1.70.0" # MSRV:3
homepage = "https://github.com/rust-lang/cargo"
repository = "https://github.com/rust-lang/cargo"
documentation = "https://docs.rs/cargo-platform"
diff --git a/src/tools/cargo/crates/cargo-test-support/src/compare.rs b/src/tools/cargo/crates/cargo-test-support/src/compare.rs
index 09e3a5a0c..d9e8d5454 100644
--- a/src/tools/cargo/crates/cargo-test-support/src/compare.rs
+++ b/src/tools/cargo/crates/cargo-test-support/src/compare.rs
@@ -236,6 +236,8 @@ fn substitute_macros(input: &str) -> String {
("[SKIPPING]", " Skipping"),
("[WAITING]", " Waiting"),
("[PUBLISHED]", " Published"),
+ ("[BLOCKING]", " Blocking"),
+ ("[GENERATED]", " Generated"),
];
let mut result = input.to_owned();
for &(pat, subst) in &macros {
diff --git a/src/tools/cargo/crates/cargo-test-support/src/diff.rs b/src/tools/cargo/crates/cargo-test-support/src/diff.rs
index 3fedc839b..cd0c97385 100644
--- a/src/tools/cargo/crates/cargo-test-support/src/diff.rs
+++ b/src/tools/cargo/crates/cargo-test-support/src/diff.rs
@@ -132,7 +132,7 @@ pub fn render_colored_changes<T: fmt::Display>(changes: &[Change<T>]) -> String
Change::Remove(i, s) => (format!("{:<4} ", i), '-', red, s),
Change::Keep(x, y, s) => (format!("{:<4}{:<4} ", x, y), ' ', dim, s),
};
- write!(
+ writeln!(
buffer,
"{dim}{nums}{reset}{bold}{sign}{reset}{color}{text}{reset}"
)
diff --git a/src/tools/cargo/crates/cargo-test-support/src/lib.rs b/src/tools/cargo/crates/cargo-test-support/src/lib.rs
index 1a8742720..ec74ce0b2 100644
--- a/src/tools/cargo/crates/cargo-test-support/src/lib.rs
+++ b/src/tools/cargo/crates/cargo-test-support/src/lib.rs
@@ -13,6 +13,7 @@ use std::path::{Path, PathBuf};
use std::process::{Command, Output};
use std::str;
use std::sync::OnceLock;
+use std::thread::JoinHandle;
use std::time::{self, Duration};
use anyhow::{bail, Result};
@@ -1470,3 +1471,50 @@ pub fn symlink_supported() -> bool {
pub fn no_such_file_err_msg() -> String {
std::io::Error::from_raw_os_error(2).to_string()
}
+
+/// Helper to retry a function `n` times.
+///
+/// The function should return `Some` when it is ready.
+pub fn retry<F, R>(n: u32, mut f: F) -> R
+where
+ F: FnMut() -> Option<R>,
+{
+ let mut count = 0;
+ let start = std::time::Instant::now();
+ loop {
+ if let Some(r) = f() {
+ return r;
+ }
+ count += 1;
+ if count > n {
+ panic!(
+ "test did not finish within {n} attempts ({:?} total)",
+ start.elapsed()
+ );
+ }
+ sleep_ms(100);
+ }
+}
+
+#[test]
+#[should_panic(expected = "test did not finish")]
+fn retry_fails() {
+ retry(2, || None::<()>);
+}
+
+/// Helper that waits for a thread to finish, up to `n` tenths of a second.
+pub fn thread_wait_timeout<T>(n: u32, thread: JoinHandle<T>) -> T {
+ retry(n, || thread.is_finished().then_some(()));
+ thread.join().unwrap()
+}
+
+/// Helper that runs some function, and waits up to `n` tenths of a second for
+/// it to finish.
+pub fn threaded_timeout<F, R>(n: u32, f: F) -> R
+where
+ F: FnOnce() -> R + Send + 'static,
+ R: Send + 'static,
+{
+ let thread = std::thread::spawn(|| f());
+ thread_wait_timeout(n, thread)
+}
diff --git a/src/tools/cargo/crates/cargo-util/Cargo.toml b/src/tools/cargo/crates/cargo-util/Cargo.toml
index cba00f917..616a79c5e 100644
--- a/src/tools/cargo/crates/cargo-util/Cargo.toml
+++ b/src/tools/cargo/crates/cargo-util/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "cargo-util"
-version = "0.2.7"
+version = "0.2.8"
rust-version.workspace = true
edition.workspace = true
license.workspace = true
@@ -10,12 +10,12 @@ description = "Miscellaneous support code used by Cargo."
[dependencies]
anyhow.workspace = true
-sha2.workspace = true
filetime.workspace = true
hex.workspace = true
jobserver.workspace = true
libc.workspace = true
same-file.workspace = true
+sha2.workspace = true
shell-escape.workspace = true
tempfile.workspace = true
tracing.workspace = true
diff --git a/src/tools/cargo/crates/cargo-util/src/paths.rs b/src/tools/cargo/crates/cargo-util/src/paths.rs
index 888ca1af5..f405c8f97 100644
--- a/src/tools/cargo/crates/cargo-util/src/paths.rs
+++ b/src/tools/cargo/crates/cargo-util/src/paths.rs
@@ -180,6 +180,19 @@ pub fn write<P: AsRef<Path>, C: AsRef<[u8]>>(path: P, contents: C) -> Result<()>
.with_context(|| format!("failed to write `{}`", path.display()))
}
+/// Writes a file to disk atomically.
+///
+/// write_atomic uses tempfile::persist to accomplish atomic writes.
+pub fn write_atomic<P: AsRef<Path>, C: AsRef<[u8]>>(path: P, contents: C) -> Result<()> {
+ let path = path.as_ref();
+ let mut tmp = TempFileBuilder::new()
+ .prefix(path.file_name().unwrap())
+ .tempfile_in(path.parent().unwrap())?;
+ tmp.write_all(contents.as_ref())?;
+ tmp.persist(path)?;
+ Ok(())
+}
+
/// Equivalent to [`write()`], but does not write anything if the file contents
/// are identical to the given contents.
pub fn write_if_changed<P: AsRef<Path>, C: AsRef<[u8]>>(path: P, contents: C) -> Result<()> {
@@ -681,7 +694,8 @@ pub fn create_dir_all_excluded_from_backups_atomic(p: impl AsRef<Path>) -> Resul
// we can infer from it's another cargo process doing work.
if let Err(e) = fs::rename(tempdir.path(), path) {
if !path.exists() {
- return Err(anyhow::Error::from(e));
+ return Err(anyhow::Error::from(e))
+ .with_context(|| format!("failed to create directory `{}`", path.display()));
}
}
Ok(())
@@ -775,6 +789,29 @@ fn exclude_from_time_machine(path: &Path) {
#[cfg(test)]
mod tests {
use super::join_paths;
+ use super::write;
+ use super::write_atomic;
+
+ #[test]
+ fn write_works() {
+ let original_contents = "[dependencies]\nfoo = 0.1.0";
+
+ let tmpdir = tempfile::tempdir().unwrap();
+ let path = tmpdir.path().join("Cargo.toml");
+ write(&path, original_contents).unwrap();
+ let contents = std::fs::read_to_string(&path).unwrap();
+ assert_eq!(contents, original_contents);
+ }
+ #[test]
+ fn write_atomic_works() {
+ let original_contents = "[dependencies]\nfoo = 0.1.0";
+
+ let tmpdir = tempfile::tempdir().unwrap();
+ let path = tmpdir.path().join("Cargo.toml");
+ write_atomic(&path, original_contents).unwrap();
+ let contents = std::fs::read_to_string(&path).unwrap();
+ assert_eq!(contents, original_contents);
+ }
#[test]
fn join_paths_lists_paths_on_error() {
diff --git a/src/tools/cargo/crates/crates-io/Cargo.toml b/src/tools/cargo/crates/crates-io/Cargo.toml
index d06dacdfa..f1b92602e 100644
--- a/src/tools/cargo/crates/crates-io/Cargo.toml
+++ b/src/tools/cargo/crates/crates-io/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "crates-io"
-version = "0.39.0"
+version = "0.39.1"
rust-version.workspace = true
edition.workspace = true
license.workspace = true
diff --git a/src/tools/cargo/crates/crates-io/lib.rs b/src/tools/cargo/crates/crates-io/lib.rs
index 757241fd3..1764ce527 100644
--- a/src/tools/cargo/crates/crates-io/lib.rs
+++ b/src/tools/cargo/crates/crates-io/lib.rs
@@ -38,6 +38,10 @@ pub struct Crate {
pub max_version: String,
}
+/// This struct is serialized as JSON and sent as metadata ahead of the crate
+/// tarball when publishing crates to a crate registry like crates.io.
+///
+/// see <https://doc.rust-lang.org/cargo/reference/registry-web-api.html#publish>
#[derive(Serialize, Deserialize)]
pub struct NewCrate {
pub name: String,
diff --git a/src/tools/cargo/crates/home/Cargo.toml b/src/tools/cargo/crates/home/Cargo.toml
index 03bd555a2..702a14e55 100644
--- a/src/tools/cargo/crates/home/Cargo.toml
+++ b/src/tools/cargo/crates/home/Cargo.toml
@@ -1,7 +1,8 @@
[package]
name = "home"
-version = "0.5.7" # also update `html_root_url` in `src/lib.rs`
+version = "0.5.8"
authors = ["Brian Anderson <andersrb@gmail.com>"]
+rust-version = "1.70.0" # MSRV:3
documentation = "https://docs.rs/home"
edition.workspace = true
include = [
diff --git a/src/tools/cargo/crates/home/src/lib.rs b/src/tools/cargo/crates/home/src/lib.rs
index 0e1e975e4..4aee7383b 100644
--- a/src/tools/cargo/crates/home/src/lib.rs
+++ b/src/tools/cargo/crates/home/src/lib.rs
@@ -18,7 +18,6 @@
//!
//! [discussion]: https://github.com/rust-lang/rust/pull/46799#issuecomment-361156935
-#![doc(html_root_url = "https://docs.rs/home/0.5.6")]
#![deny(rust_2018_idioms)]
pub mod env;
diff --git a/src/tools/cargo/crates/resolver-tests/src/lib.rs b/src/tools/cargo/crates/resolver-tests/src/lib.rs
index 9bdeb8674..e2cbcee62 100644
--- a/src/tools/cargo/crates/resolver-tests/src/lib.rs
+++ b/src/tools/cargo/crates/resolver-tests/src/lib.rs
@@ -12,7 +12,7 @@ use std::task::Poll;
use std::time::Instant;
use cargo::core::dependency::DepKind;
-use cargo::core::resolver::{self, ResolveOpts, VersionPreferences};
+use cargo::core::resolver::{self, ResolveOpts, VersionOrdering, VersionPreferences};
use cargo::core::Resolve;
use cargo::core::{Dependency, PackageId, Registry, Summary};
use cargo::core::{GitReference, SourceId};
@@ -190,15 +190,17 @@ pub fn resolve_with_config_raw(
.unwrap();
let opts = ResolveOpts::everything();
let start = Instant::now();
- let max_rust_version = None;
+ let mut version_prefs = VersionPreferences::default();
+ if config.cli_unstable().minimal_versions {
+ version_prefs.version_ordering(VersionOrdering::MinimumVersionsFirst)
+ }
let resolve = resolver::resolve(
&[(summary, opts)],
&[],
&mut registry,
- &VersionPreferences::default(),
+ &version_prefs,
Some(config),
true,
- max_rust_version,
);
// The largest test in our suite takes less then 30 sec.
@@ -982,14 +984,17 @@ fn meta_test_multiple_versions_strategy() {
/// Assert `xs` contains `elems`
#[track_caller]
-pub fn assert_contains<A: PartialEq>(xs: &[A], elems: &[A]) {
+pub fn assert_contains<A: PartialEq + std::fmt::Debug>(xs: &[A], elems: &[A]) {
for elem in elems {
- assert!(xs.contains(elem));
+ assert!(
+ xs.contains(elem),
+ "missing element\nset: {xs:?}\nmissing: {elem:?}"
+ );
}
}
#[track_caller]
-pub fn assert_same<A: PartialEq>(a: &[A], b: &[A]) {
- assert_eq!(a.len(), b.len());
+pub fn assert_same<A: PartialEq + std::fmt::Debug>(a: &[A], b: &[A]) {
+ assert_eq!(a.len(), b.len(), "not equal\n{a:?}\n{b:?}");
assert_contains(b, a);
}
diff --git a/src/tools/cargo/crates/xtask-bump-check/Cargo.toml b/src/tools/cargo/crates/xtask-bump-check/Cargo.toml
index e878f7dda..c8a472adc 100644
--- a/src/tools/cargo/crates/xtask-bump-check/Cargo.toml
+++ b/src/tools/cargo/crates/xtask-bump-check/Cargo.toml
@@ -11,5 +11,6 @@ cargo.workspace = true
cargo-util.workspace = true
clap.workspace = true
git2.workspace = true
-tracing.workspace = true
+semver.workspace = true
tracing-subscriber.workspace = true
+tracing.workspace = true
diff --git a/src/tools/cargo/crates/xtask-bump-check/src/xtask.rs b/src/tools/cargo/crates/xtask-bump-check/src/xtask.rs
index 4bf3f03d5..b99ac8b32 100644
--- a/src/tools/cargo/crates/xtask-bump-check/src/xtask.rs
+++ b/src/tools/cargo/crates/xtask-bump-check/src/xtask.rs
@@ -22,8 +22,8 @@ use cargo::core::Registry;
use cargo::core::SourceId;
use cargo::core::Workspace;
use cargo::sources::source::QueryKind;
+use cargo::util::cache_lock::CacheLockMode;
use cargo::util::command_prelude::*;
-use cargo::util::ToSemver;
use cargo::CargoResult;
use cargo_util::ProcessBuilder;
@@ -148,26 +148,13 @@ fn bump_check(args: &clap::ArgMatches, config: &cargo::util::Config) -> CargoRes
anyhow::bail!(msg)
}
- // Tracked by https://github.com/obi1kenobi/cargo-semver-checks/issues/511
- let exclude_args = [
- "--exclude",
- "cargo-credential-1password",
- "--exclude",
- "cargo-credential-libsecret",
- "--exclude",
- "cargo-credential-macos-keychain",
- "--exclude",
- "cargo-credential-wincred",
- ];
-
// Even when we test against baseline-rev, we still need to make sure a
// change doesn't violate SemVer rules against crates.io releases. The
// possibility of this happening is nearly zero but no harm to check twice.
let mut cmd = ProcessBuilder::new("cargo");
cmd.arg("semver-checks")
.arg("check-release")
- .arg("--workspace")
- .args(&exclude_args);
+ .arg("--workspace");
config.shell().status("Running", &cmd)?;
cmd.exec()?;
@@ -176,8 +163,7 @@ fn bump_check(args: &clap::ArgMatches, config: &cargo::util::Config) -> CargoRes
cmd.arg("semver-checks")
.arg("--workspace")
.arg("--baseline-rev")
- .arg(referenced_commit.id().to_string())
- .args(&exclude_args);
+ .arg(referenced_commit.id().to_string());
config.shell().status("Running", &cmd)?;
cmd.exec()?;
}
@@ -290,7 +276,7 @@ fn beta_and_stable_branch(repo: &git2::Repository) -> CargoResult<[git2::Branch<
tracing::trace!("branch `{name}` is not in the format of `<remote>/rust-<semver>`");
continue;
};
- let Ok(version) = version.to_semver() else {
+ let Ok(version) = version.parse::<semver::Version>() else {
tracing::trace!("branch `{name}` is not a valid semver: `{version}`");
continue;
};
@@ -361,7 +347,7 @@ fn check_crates_io<'a>(
) -> CargoResult<()> {
let source_id = SourceId::crates_io(config)?;
let mut registry = PackageRegistry::new(config)?;
- let _lock = config.acquire_package_cache_lock()?;
+ let _lock = config.acquire_package_cache_lock(CacheLockMode::DownloadExclusive)?;
registry.lock_patches();
config.shell().status(
STATUS,
diff --git a/src/tools/cargo/credential/cargo-credential-1password/Cargo.toml b/src/tools/cargo/credential/cargo-credential-1password/Cargo.toml
index d7bd949d1..9e5b1e635 100644
--- a/src/tools/cargo/credential/cargo-credential-1password/Cargo.toml
+++ b/src/tools/cargo/credential/cargo-credential-1password/Cargo.toml
@@ -1,8 +1,9 @@
[package]
name = "cargo-credential-1password"
-version = "0.4.0"
+version = "0.4.2"
edition.workspace = true
license.workspace = true
+rust-version = "1.70.0" # MSRV:3
repository = "https://github.com/rust-lang/cargo"
description = "A Cargo credential process that stores tokens in a 1password vault."
diff --git a/src/tools/cargo/credential/cargo-credential-1password/LICENSE-APACHE b/src/tools/cargo/credential/cargo-credential-1password/LICENSE-APACHE
new file mode 120000
index 000000000..1cd601d0a
--- /dev/null
+++ b/src/tools/cargo/credential/cargo-credential-1password/LICENSE-APACHE
@@ -0,0 +1 @@
+../../LICENSE-APACHE \ No newline at end of file
diff --git a/src/tools/cargo/credential/cargo-credential-1password/LICENSE-MIT b/src/tools/cargo/credential/cargo-credential-1password/LICENSE-MIT
new file mode 120000
index 000000000..b2cfbdc7b
--- /dev/null
+++ b/src/tools/cargo/credential/cargo-credential-1password/LICENSE-MIT
@@ -0,0 +1 @@
+../../LICENSE-MIT \ No newline at end of file
diff --git a/src/tools/cargo/credential/cargo-credential-1password/README.md b/src/tools/cargo/credential/cargo-credential-1password/README.md
index 3648efe4b..fc3c9460a 100644
--- a/src/tools/cargo/credential/cargo-credential-1password/README.md
+++ b/src/tools/cargo/credential/cargo-credential-1password/README.md
@@ -2,17 +2,31 @@
A Cargo [credential provider] for [1password].
-`cargo-credential-1password` uses the 1password `op` CLI to store the token. You must
-install the `op` CLI from the [1password
-website](https://1password.com/downloads/command-line/). You must run `op signin`
-at least once with the appropriate arguments (such as `op signin my.1password.com user@example.com`),
-unless you provide the sign-in-address and email arguments. The master password will be required on each request
-unless the appropriate `OP_SESSION` environment variable is set. It supports
-the following command-line arguments:
-* `--account`: The account shorthand name to use.
-* `--vault`: The vault name to use.
-* `--sign-in-address`: The sign-in-address, which is a web address such as `my.1password.com`.
-* `--email`: The email address to sign in with.
+## Usage
+
+`cargo-credential-1password` uses the 1password `op` CLI to store the token. You
+must install the `op` CLI from the [1password
+website](https://1password.com/downloads/command-line/).
+
+Afterward you need to configure `cargo` to use `cargo-credential-1password` as
+the credential provider. You can do this by adding something like the following
+to your [cargo config file][credential provider]:
+
+```toml
+[registry]
+global-credential-providers = ["cargo-credential-1password --account my.1password.com"]
+```
+
+Finally, run `cargo login` to save your registry token in 1password.
+
+## CLI Arguments
+
+`cargo-credential-1password` supports the following command-line arguments:
+
+* `--account`: The account name to use. For a list of available accounts,
+ run `op account list`.
+* `--vault`: The vault name to use. For a list of available vaults,
+ run `op vault list`.
[1password]: https://1password.com/
-[credential provider]: https://doc.rust-lang.org/nightly/cargo/reference/registry-authentication.html
+[credential provider]: https://doc.rust-lang.org/stable/cargo/reference/registry-authentication.html
diff --git a/src/tools/cargo/credential/cargo-credential-1password/src/main.rs b/src/tools/cargo/credential/cargo-credential-1password/src/main.rs
index 921b52145..321a99c51 100644
--- a/src/tools/cargo/credential/cargo-credential-1password/src/main.rs
+++ b/src/tools/cargo/credential/cargo-credential-1password/src/main.rs
@@ -79,6 +79,10 @@ impl OnePasswordKeychain {
}
let mut cmd = Command::new("op");
cmd.args(["signin", "--raw"]);
+ if let Some(account) = &self.account {
+ cmd.arg("--account");
+ cmd.arg(account);
+ }
cmd.stdout(Stdio::piped());
let mut child = cmd
.spawn()
diff --git a/src/tools/cargo/credential/cargo-credential-libsecret/Cargo.toml b/src/tools/cargo/credential/cargo-credential-libsecret/Cargo.toml
index 5bedad3b9..19ef33a34 100644
--- a/src/tools/cargo/credential/cargo-credential-libsecret/Cargo.toml
+++ b/src/tools/cargo/credential/cargo-credential-libsecret/Cargo.toml
@@ -1,8 +1,9 @@
[package]
name = "cargo-credential-libsecret"
-version = "0.3.2"
+version = "0.4.1"
edition.workspace = true
license.workspace = true
+rust-version.workspace = true
repository = "https://github.com/rust-lang/cargo"
description = "A Cargo credential process that stores tokens with GNOME libsecret."
diff --git a/src/tools/cargo/credential/cargo-credential-libsecret/LICENSE-APACHE b/src/tools/cargo/credential/cargo-credential-libsecret/LICENSE-APACHE
new file mode 120000
index 000000000..1cd601d0a
--- /dev/null
+++ b/src/tools/cargo/credential/cargo-credential-libsecret/LICENSE-APACHE
@@ -0,0 +1 @@
+../../LICENSE-APACHE \ No newline at end of file
diff --git a/src/tools/cargo/credential/cargo-credential-libsecret/LICENSE-MIT b/src/tools/cargo/credential/cargo-credential-libsecret/LICENSE-MIT
new file mode 120000
index 000000000..b2cfbdc7b
--- /dev/null
+++ b/src/tools/cargo/credential/cargo-credential-libsecret/LICENSE-MIT
@@ -0,0 +1 @@
+../../LICENSE-MIT \ No newline at end of file
diff --git a/src/tools/cargo/credential/cargo-credential-macos-keychain/Cargo.toml b/src/tools/cargo/credential/cargo-credential-macos-keychain/Cargo.toml
index 172e9c10b..4dec8def6 100644
--- a/src/tools/cargo/credential/cargo-credential-macos-keychain/Cargo.toml
+++ b/src/tools/cargo/credential/cargo-credential-macos-keychain/Cargo.toml
@@ -1,8 +1,9 @@
[package]
name = "cargo-credential-macos-keychain"
-version = "0.3.1"
+version = "0.4.1"
edition.workspace = true
license.workspace = true
+rust-version.workspace = true
repository = "https://github.com/rust-lang/cargo"
description = "A Cargo credential process that stores tokens in a macOS keychain."
diff --git a/src/tools/cargo/credential/cargo-credential-macos-keychain/LICENSE-APACHE b/src/tools/cargo/credential/cargo-credential-macos-keychain/LICENSE-APACHE
new file mode 120000
index 000000000..1cd601d0a
--- /dev/null
+++ b/src/tools/cargo/credential/cargo-credential-macos-keychain/LICENSE-APACHE
@@ -0,0 +1 @@
+../../LICENSE-APACHE \ No newline at end of file
diff --git a/src/tools/cargo/credential/cargo-credential-macos-keychain/LICENSE-MIT b/src/tools/cargo/credential/cargo-credential-macos-keychain/LICENSE-MIT
new file mode 120000
index 000000000..b2cfbdc7b
--- /dev/null
+++ b/src/tools/cargo/credential/cargo-credential-macos-keychain/LICENSE-MIT
@@ -0,0 +1 @@
+../../LICENSE-MIT \ No newline at end of file
diff --git a/src/tools/cargo/credential/cargo-credential-wincred/Cargo.toml b/src/tools/cargo/credential/cargo-credential-wincred/Cargo.toml
index 6da6578a5..c904075bb 100644
--- a/src/tools/cargo/credential/cargo-credential-wincred/Cargo.toml
+++ b/src/tools/cargo/credential/cargo-credential-wincred/Cargo.toml
@@ -1,8 +1,9 @@
[package]
name = "cargo-credential-wincred"
-version = "0.3.1"
+version = "0.4.1"
edition.workspace = true
license.workspace = true
+rust-version.workspace = true
repository = "https://github.com/rust-lang/cargo"
description = "A Cargo credential process that stores tokens with Windows Credential Manager."
diff --git a/src/tools/cargo/credential/cargo-credential-wincred/LICENSE-APACHE b/src/tools/cargo/credential/cargo-credential-wincred/LICENSE-APACHE
new file mode 120000
index 000000000..1cd601d0a
--- /dev/null
+++ b/src/tools/cargo/credential/cargo-credential-wincred/LICENSE-APACHE
@@ -0,0 +1 @@
+../../LICENSE-APACHE \ No newline at end of file
diff --git a/src/tools/cargo/credential/cargo-credential-wincred/LICENSE-MIT b/src/tools/cargo/credential/cargo-credential-wincred/LICENSE-MIT
new file mode 120000
index 000000000..b2cfbdc7b
--- /dev/null
+++ b/src/tools/cargo/credential/cargo-credential-wincred/LICENSE-MIT
@@ -0,0 +1 @@
+../../LICENSE-MIT \ No newline at end of file
diff --git a/src/tools/cargo/credential/cargo-credential/Cargo.toml b/src/tools/cargo/credential/cargo-credential/Cargo.toml
index c8db996bf..8ba65b8b9 100644
--- a/src/tools/cargo/credential/cargo-credential/Cargo.toml
+++ b/src/tools/cargo/credential/cargo-credential/Cargo.toml
@@ -1,9 +1,9 @@
[package]
name = "cargo-credential"
-version = "0.4.0"
+version = "0.4.1"
edition.workspace = true
license.workspace = true
-rust-version = "1.70.0"
+rust-version = "1.70.0" # MSRV:3
repository = "https://github.com/rust-lang/cargo"
description = "A library to assist writing Cargo credential helpers."
diff --git a/src/tools/cargo/credential/cargo-credential/LICENSE-APACHE b/src/tools/cargo/credential/cargo-credential/LICENSE-APACHE
new file mode 120000
index 000000000..1cd601d0a
--- /dev/null
+++ b/src/tools/cargo/credential/cargo-credential/LICENSE-APACHE
@@ -0,0 +1 @@
+../../LICENSE-APACHE \ No newline at end of file
diff --git a/src/tools/cargo/credential/cargo-credential/LICENSE-MIT b/src/tools/cargo/credential/cargo-credential/LICENSE-MIT
new file mode 120000
index 000000000..b2cfbdc7b
--- /dev/null
+++ b/src/tools/cargo/credential/cargo-credential/LICENSE-MIT
@@ -0,0 +1 @@
+../../LICENSE-MIT \ No newline at end of file
diff --git a/src/tools/cargo/src/bin/cargo/cli.rs b/src/tools/cargo/src/bin/cargo/cli.rs
index 06b4a20e2..a21030f01 100644
--- a/src/tools/cargo/src/bin/cargo/cli.rs
+++ b/src/tools/cargo/src/bin/cargo/cli.rs
@@ -2,7 +2,7 @@ use anyhow::{anyhow, Context as _};
use cargo::core::shell::Shell;
use cargo::core::{features, CliUnstable};
use cargo::{self, drop_print, drop_println, CargoResult, CliResult, Config};
-use clap::{Arg, ArgMatches};
+use clap::{builder::UnknownArgumentValueParser, Arg, ArgMatches};
use itertools::Itertools;
use std::collections::HashMap;
use std::ffi::OsStr;
@@ -618,15 +618,29 @@ See '<cyan,bold>cargo help</> <cyan><<command>></>' for more information on a sp
.help_heading(heading::MANIFEST_OPTIONS)
.global(true),
)
+ // Better suggestion for the unsupported short config flag.
+ .arg( Arg::new("unsupported-short-config-flag")
+ .help("")
+ .short('c')
+ .value_parser(UnknownArgumentValueParser::suggest_arg("--config"))
+ .action(ArgAction::SetTrue)
+ .global(true)
+ .hide(true))
.arg(multi_opt("config", "KEY=VALUE", "Override a configuration value").global(true))
- .arg(
- Arg::new("unstable-features")
- .help("Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details")
- .short('Z')
- .value_name("FLAG")
- .action(ArgAction::Append)
- .global(true),
- )
+ // Better suggestion for the unsupported lowercase unstable feature flag.
+ .arg( Arg::new("unsupported-lowercase-unstable-feature-flag")
+ .help("")
+ .short('z')
+ .value_parser(UnknownArgumentValueParser::suggest_arg("-Z"))
+ .action(ArgAction::SetTrue)
+ .global(true)
+ .hide(true))
+ .arg(Arg::new("unstable-features")
+ .help("Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details")
+ .short('Z')
+ .value_name("FLAG")
+ .action(ArgAction::Append)
+ .global(true))
.subcommands(commands::builtin())
}
diff --git a/src/tools/cargo/src/bin/cargo/commands/add.rs b/src/tools/cargo/src/bin/cargo/commands/add.rs
index 344cd2af3..e1ece14b8 100644
--- a/src/tools/cargo/src/bin/cargo/commands/add.rs
+++ b/src/tools/cargo/src/bin/cargo/commands/add.rs
@@ -77,7 +77,7 @@ Example uses:
"Ignore `rust-version` specification in packages (unstable)"
),
])
- .arg_manifest_path()
+ .arg_manifest_path_without_unsupported_path_tip()
.arg_package("Package to modify")
.arg_dry_run("Don't actually write the manifest")
.arg_quiet()
diff --git a/src/tools/cargo/src/bin/cargo/commands/bench.rs b/src/tools/cargo/src/bin/cargo/commands/bench.rs
index 1a97d84a4..85c975a6c 100644
--- a/src/tools/cargo/src/bin/cargo/commands/bench.rs
+++ b/src/tools/cargo/src/bin/cargo/commands/bench.rs
@@ -12,6 +12,7 @@ pub fn cli() -> Command {
)
.arg(
Arg::new("args")
+ .value_name("ARGS")
.help("Arguments for the bench binary")
.num_args(0..)
.last(true),
@@ -36,9 +37,9 @@ pub fn cli() -> Command {
"Benchmark only the specified example",
"Benchmark all examples",
"Benchmark only the specified test target",
- "Benchmark all tests",
+ "Benchmark all test targets",
"Benchmark only the specified bench target",
- "Benchmark all benches",
+ "Benchmark all bench targets",
"Benchmark all targets",
)
.arg_features()
diff --git a/src/tools/cargo/src/bin/cargo/commands/build.rs b/src/tools/cargo/src/bin/cargo/commands/build.rs
index d503f43dd..e2ed87d1b 100644
--- a/src/tools/cargo/src/bin/cargo/commands/build.rs
+++ b/src/tools/cargo/src/bin/cargo/commands/build.rs
@@ -23,9 +23,9 @@ pub fn cli() -> Command {
"Build only the specified example",
"Build all examples",
"Build only the specified test target",
- "Build all tests",
+ "Build all test targets",
"Build only the specified bench target",
- "Build all benches",
+ "Build all bench targets",
"Build all targets",
)
.arg_features()
@@ -35,14 +35,7 @@ pub fn cli() -> Command {
.arg_parallel()
.arg_target_triple("Build for the target triple")
.arg_target_dir()
- .arg(
- opt(
- "out-dir",
- "Copy final artifacts to this directory (unstable)",
- )
- .value_name("PATH")
- .help_heading(heading::COMPILATION_OPTIONS),
- )
+ .arg_out_dir()
.arg_build_plan()
.arg_unit_graph()
.arg_timings()
diff --git a/src/tools/cargo/src/bin/cargo/commands/check.rs b/src/tools/cargo/src/bin/cargo/commands/check.rs
index a54e84cdc..77e2b9280 100644
--- a/src/tools/cargo/src/bin/cargo/commands/check.rs
+++ b/src/tools/cargo/src/bin/cargo/commands/check.rs
@@ -23,9 +23,9 @@ pub fn cli() -> Command {
"Check only the specified example",
"Check all examples",
"Check only the specified test target",
- "Check all tests",
+ "Check all test targets",
"Check only the specified bench target",
- "Check all benches",
+ "Check all bench targets",
"Check all targets",
)
.arg_features()
diff --git a/src/tools/cargo/src/bin/cargo/commands/fix.rs b/src/tools/cargo/src/bin/cargo/commands/fix.rs
index 0ecf47450..bd938dbc7 100644
--- a/src/tools/cargo/src/bin/cargo/commands/fix.rs
+++ b/src/tools/cargo/src/bin/cargo/commands/fix.rs
@@ -41,9 +41,9 @@ pub fn cli() -> Command {
"Fix only the specified example",
"Fix all examples",
"Fix only the specified test target",
- "Fix all tests",
+ "Fix all test targets",
"Fix only the specified bench target",
- "Fix all benches",
+ "Fix all bench targets",
"Fix all targets (default)",
)
.arg_features()
diff --git a/src/tools/cargo/src/bin/cargo/commands/init.rs b/src/tools/cargo/src/bin/cargo/commands/init.rs
index 48409d827..04dd7ae45 100644
--- a/src/tools/cargo/src/bin/cargo/commands/init.rs
+++ b/src/tools/cargo/src/bin/cargo/commands/init.rs
@@ -5,7 +5,12 @@ use cargo::ops;
pub fn cli() -> Command {
subcommand("init")
.about("Create a new cargo package in an existing directory")
- .arg(Arg::new("path").action(ArgAction::Set).default_value("."))
+ .arg(
+ Arg::new("path")
+ .value_name("PATH")
+ .action(ArgAction::Set)
+ .default_value("."),
+ )
.arg_new_opts()
.arg_registry("Registry to use")
.arg_quiet()
diff --git a/src/tools/cargo/src/bin/cargo/commands/install.rs b/src/tools/cargo/src/bin/cargo/commands/install.rs
index 2e5163bdc..cb66ba100 100644
--- a/src/tools/cargo/src/bin/cargo/commands/install.rs
+++ b/src/tools/cargo/src/bin/cargo/commands/install.rs
@@ -6,9 +6,9 @@ use anyhow::format_err;
use cargo::core::{GitReference, SourceId, Workspace};
use cargo::ops;
use cargo::util::IntoUrl;
-use cargo::util::ToSemver;
-use cargo::util::VersionReqExt;
+use cargo::util_semver::VersionExt;
use cargo::CargoResult;
+use itertools::Itertools;
use semver::VersionReq;
use cargo_util::paths;
@@ -16,7 +16,13 @@ use cargo_util::paths;
pub fn cli() -> Command {
subcommand("install")
.about("Install a Rust binary. Default location is $HOME/.cargo/bin")
- .arg(Arg::new("crate").value_parser(parse_crate).num_args(0..))
+ .arg(
+ Arg::new("crate")
+ .value_name("CRATE[@<VER>]")
+ .help("Select the package from the given source")
+ .value_parser(parse_crate)
+ .num_args(0..),
+ )
.arg(
opt("version", "Specify a version to install")
.alias("vers")
@@ -113,6 +119,7 @@ pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult {
.get_many::<CrateVersion>("crate")
.unwrap_or_default()
.cloned()
+ .dedup_by(|x, y| x == y)
.map(|(krate, local_version)| resolve_crate(krate, local_version, version))
.collect::<crate::CargoResult<Vec<_>>>()?;
@@ -256,8 +263,8 @@ fn parse_semver_flag(v: &str) -> CargoResult<VersionReq> {
),
}
} else {
- match v.to_semver() {
- Ok(v) => Ok(VersionReq::exact(&v)),
+ match v.trim().parse::<semver::Version>() {
+ Ok(v) => Ok(v.to_exact_req()),
Err(e) => {
let mut msg = e.to_string();
diff --git a/src/tools/cargo/src/bin/cargo/commands/login.rs b/src/tools/cargo/src/bin/cargo/commands/login.rs
index 118b03310..877ec6aeb 100644
--- a/src/tools/cargo/src/bin/cargo/commands/login.rs
+++ b/src/tools/cargo/src/bin/cargo/commands/login.rs
@@ -6,7 +6,7 @@ use crate::command_prelude::*;
pub fn cli() -> Command {
subcommand("login")
.about("Log in to a registry.")
- .arg(Arg::new("token").action(ArgAction::Set))
+ .arg(Arg::new("token").value_name("TOKEN").action(ArgAction::Set))
.arg_registry("Registry to use")
.arg(
Arg::new("args")
diff --git a/src/tools/cargo/src/bin/cargo/commands/new.rs b/src/tools/cargo/src/bin/cargo/commands/new.rs
index c8d19218f..0ab093012 100644
--- a/src/tools/cargo/src/bin/cargo/commands/new.rs
+++ b/src/tools/cargo/src/bin/cargo/commands/new.rs
@@ -5,7 +5,12 @@ use cargo::ops;
pub fn cli() -> Command {
subcommand("new")
.about("Create a new cargo package at <path>")
- .arg(Arg::new("path").action(ArgAction::Set).required(true))
+ .arg(
+ Arg::new("path")
+ .value_name("PATH")
+ .action(ArgAction::Set)
+ .required(true),
+ )
.arg_new_opts()
.arg_registry("Registry to use")
.arg_quiet()
diff --git a/src/tools/cargo/src/bin/cargo/commands/owner.rs b/src/tools/cargo/src/bin/cargo/commands/owner.rs
index 00080b829..b787d094c 100644
--- a/src/tools/cargo/src/bin/cargo/commands/owner.rs
+++ b/src/tools/cargo/src/bin/cargo/commands/owner.rs
@@ -6,7 +6,7 @@ use cargo_credential::Secret;
pub fn cli() -> Command {
subcommand("owner")
.about("Manage the owners of a crate on the registry")
- .arg(Arg::new("crate").action(ArgAction::Set))
+ .arg(Arg::new("crate").value_name("CRATE").action(ArgAction::Set))
.arg(
multi_opt(
"add",
diff --git a/src/tools/cargo/src/bin/cargo/commands/pkgid.rs b/src/tools/cargo/src/bin/cargo/commands/pkgid.rs
index 9d7a6c712..2d1d41325 100644
--- a/src/tools/cargo/src/bin/cargo/commands/pkgid.rs
+++ b/src/tools/cargo/src/bin/cargo/commands/pkgid.rs
@@ -6,7 +6,7 @@ use cargo::util::print_available_packages;
pub fn cli() -> Command {
subcommand("pkgid")
.about("Print a fully qualified package specification")
- .arg(Arg::new("spec").action(ArgAction::Set))
+ .arg(Arg::new("spec").value_name("SPEC").action(ArgAction::Set))
.arg_quiet()
.arg_package("Argument to get the package ID specifier for")
.arg_manifest_path()
diff --git a/src/tools/cargo/src/bin/cargo/commands/remove.rs b/src/tools/cargo/src/bin/cargo/commands/remove.rs
index c6508a6b2..c115291cb 100644
--- a/src/tools/cargo/src/bin/cargo/commands/remove.rs
+++ b/src/tools/cargo/src/bin/cargo/commands/remove.rs
@@ -34,19 +34,19 @@ pub fn cli() -> clap::Command {
.conflicts_with("build")
.action(clap::ArgAction::SetTrue)
.group("section")
- .help("Remove as development dependency"),
+ .help("Remove from dev-dependencies"),
clap::Arg::new("build")
.long("build")
.conflicts_with("dev")
.action(clap::ArgAction::SetTrue)
.group("section")
- .help("Remove as build dependency"),
+ .help("Remove from build-dependencies"),
clap::Arg::new("target")
.long("target")
.num_args(1)
.value_name("TARGET")
.value_parser(clap::builder::NonEmptyStringValueParser::new())
- .help("Remove as dependency from the given target platform"),
+ .help("Remove from target-dependencies"),
])
.arg_package("Package to remove from")
.arg_manifest_path()
@@ -270,7 +270,10 @@ fn gc_workspace(workspace: &Workspace<'_>) -> CargoResult<()> {
}
if is_modified {
- cargo_util::paths::write(workspace.root_manifest(), manifest.to_string().as_bytes())?;
+ cargo_util::paths::write_atomic(
+ workspace.root_manifest(),
+ manifest.to_string().as_bytes(),
+ )?;
}
Ok(())
@@ -283,7 +286,7 @@ fn spec_has_match(
config: &Config,
) -> CargoResult<bool> {
for dep in dependencies {
- if spec.name().as_str() != &dep.name {
+ if spec.name() != &dep.name {
continue;
}
diff --git a/src/tools/cargo/src/bin/cargo/commands/run.rs b/src/tools/cargo/src/bin/cargo/commands/run.rs
index 029c9ee56..94396e63f 100644
--- a/src/tools/cargo/src/bin/cargo/commands/run.rs
+++ b/src/tools/cargo/src/bin/cargo/commands/run.rs
@@ -16,6 +16,7 @@ pub fn cli() -> Command {
.about("Run a binary or example of the local package")
.arg(
Arg::new("args")
+ .value_name("ARGS")
.help("Arguments for the binary or example to run")
.value_parser(value_parser!(OsString))
.num_args(0..)
diff --git a/src/tools/cargo/src/bin/cargo/commands/rustc.rs b/src/tools/cargo/src/bin/cargo/commands/rustc.rs
index 60f0b9d60..9b6a57577 100644
--- a/src/tools/cargo/src/bin/cargo/commands/rustc.rs
+++ b/src/tools/cargo/src/bin/cargo/commands/rustc.rs
@@ -10,6 +10,7 @@ pub fn cli() -> Command {
.about("Compile a package, and pass extra options to the compiler")
.arg(
Arg::new("args")
+ .value_name("ARGS")
.num_args(0..)
.help("Extra rustc flags")
.trailing_var_arg(true),
@@ -38,9 +39,9 @@ pub fn cli() -> Command {
"Build only the specified example",
"Build all examples",
"Build only the specified test target",
- "Build all tests",
+ "Build all test targets",
"Build only the specified bench target",
- "Build all benches",
+ "Build all bench targets",
"Build all targets",
)
.arg_features()
diff --git a/src/tools/cargo/src/bin/cargo/commands/rustdoc.rs b/src/tools/cargo/src/bin/cargo/commands/rustdoc.rs
index 8cb2f10de..72de57ad0 100644
--- a/src/tools/cargo/src/bin/cargo/commands/rustdoc.rs
+++ b/src/tools/cargo/src/bin/cargo/commands/rustdoc.rs
@@ -7,6 +7,7 @@ pub fn cli() -> Command {
.about("Build a package's documentation, using specified custom flags.")
.arg(
Arg::new("args")
+ .value_name("ARGS")
.help("Extra rustdoc flags")
.num_args(0..)
.trailing_var_arg(true),
@@ -26,9 +27,9 @@ pub fn cli() -> Command {
"Build only the specified example",
"Build all examples",
"Build only the specified test target",
- "Build all tests",
+ "Build all test targets",
"Build only the specified bench target",
- "Build all benches",
+ "Build all bench targets",
"Build all targets",
)
.arg_features()
diff --git a/src/tools/cargo/src/bin/cargo/commands/search.rs b/src/tools/cargo/src/bin/cargo/commands/search.rs
index 9cacfc7e8..377aa84e1 100644
--- a/src/tools/cargo/src/bin/cargo/commands/search.rs
+++ b/src/tools/cargo/src/bin/cargo/commands/search.rs
@@ -7,7 +7,7 @@ use cargo::ops;
pub fn cli() -> Command {
subcommand("search")
.about("Search packages in crates.io")
- .arg(Arg::new("query").num_args(0..))
+ .arg(Arg::new("query").value_name("QUERY").num_args(0..))
.arg(
opt(
"limit",
diff --git a/src/tools/cargo/src/bin/cargo/commands/test.rs b/src/tools/cargo/src/bin/cargo/commands/test.rs
index 3c7af506d..6e8aff043 100644
--- a/src/tools/cargo/src/bin/cargo/commands/test.rs
+++ b/src/tools/cargo/src/bin/cargo/commands/test.rs
@@ -13,6 +13,7 @@ pub fn cli() -> Command {
)
.arg(
Arg::new("args")
+ .value_name("ARGS")
.help("Arguments for the test binary")
.num_args(0..)
.last(true),
@@ -42,9 +43,9 @@ pub fn cli() -> Command {
"Test only the specified example",
"Test all examples",
"Test only the specified test target",
- "Test all tests",
+ "Test all test targets",
"Test only the specified bench target",
- "Test all benches",
+ "Test all bench targets",
"Test all targets (does not include doctests)",
)
.arg_features()
diff --git a/src/tools/cargo/src/bin/cargo/commands/uninstall.rs b/src/tools/cargo/src/bin/cargo/commands/uninstall.rs
index 9585d290b..30833f292 100644
--- a/src/tools/cargo/src/bin/cargo/commands/uninstall.rs
+++ b/src/tools/cargo/src/bin/cargo/commands/uninstall.rs
@@ -5,7 +5,7 @@ use cargo::ops;
pub fn cli() -> Command {
subcommand("uninstall")
.about("Remove a Rust binary")
- .arg(Arg::new("spec").num_args(0..))
+ .arg(Arg::new("spec").value_name("SPEC").num_args(0..))
.arg(opt("root", "Directory to uninstall packages from").value_name("DIR"))
.arg_quiet()
.arg_package_spec_simple("Package to uninstall")
diff --git a/src/tools/cargo/src/bin/cargo/commands/yank.rs b/src/tools/cargo/src/bin/cargo/commands/yank.rs
index 62d1821c3..75a1772ca 100644
--- a/src/tools/cargo/src/bin/cargo/commands/yank.rs
+++ b/src/tools/cargo/src/bin/cargo/commands/yank.rs
@@ -6,7 +6,7 @@ use cargo_credential::Secret;
pub fn cli() -> Command {
subcommand("yank")
.about("Remove a pushed crate from the index")
- .arg(Arg::new("crate").action(ArgAction::Set))
+ .arg(Arg::new("crate").value_name("CRATE").action(ArgAction::Set))
.arg(
opt("version", "The version to yank or un-yank")
.alias("vers")
diff --git a/src/tools/cargo/src/bin/cargo/main.rs b/src/tools/cargo/src/bin/cargo/main.rs
index 16e4f24f3..245622b6c 100644
--- a/src/tools/cargo/src/bin/cargo/main.rs
+++ b/src/tools/cargo/src/bin/cargo/main.rs
@@ -4,7 +4,7 @@
use cargo::util::network::http::http_handle;
use cargo::util::network::http::needs_custom_http_transport;
-use cargo::util::toml::StringOrVec;
+use cargo::util::toml::schema::StringOrVec;
use cargo::util::CliError;
use cargo::util::{self, closest_msg, command_prelude, CargoResult, CliResult, Config};
use cargo_util::{ProcessBuilder, ProcessError};
@@ -192,10 +192,9 @@ fn execute_external_subcommand(config: &Config, cmd: &str, args: &[&OsStr]) -> C
let did_you_mean = closest_msg(cmd, suggestions.keys(), |c| c);
anyhow::format_err!(
- "no such command: `{}`{}\n\n\t\
- View all installed commands with `cargo --list`",
- cmd,
- did_you_mean
+ "no such command: `{cmd}`{did_you_mean}\n\n\t\
+ View all installed commands with `cargo --list`\n\t\
+ Find a package to install `{cmd}` with `cargo search cargo-{cmd}`",
)
};
diff --git a/src/tools/cargo/src/cargo/core/compiler/context/mod.rs b/src/tools/cargo/src/cargo/core/compiler/context/mod.rs
index 010fe2793..cfbfccb30 100644
--- a/src/tools/cargo/src/cargo/core/compiler/context/mod.rs
+++ b/src/tools/cargo/src/cargo/core/compiler/context/mod.rs
@@ -7,6 +7,7 @@ use std::sync::{Arc, Mutex};
use crate::core::compiler::compilation::{self, UnitOutput};
use crate::core::compiler::{self, artifact, Unit};
use crate::core::PackageId;
+use crate::util::cache_lock::CacheLockMode;
use crate::util::errors::CargoResult;
use crate::util::profile;
use anyhow::{bail, Context as _};
@@ -132,6 +133,13 @@ impl<'a, 'cfg> Context<'a, 'cfg> {
///
/// [`ops::cargo_compile`]: ../../../ops/cargo_compile/index.html
pub fn compile(mut self, exec: &Arc<dyn Executor>) -> CargoResult<Compilation<'cfg>> {
+ // A shared lock is held during the duration of the build since rustc
+ // needs to read from the `src` cache, and we don't want other
+ // commands modifying the `src` cache while it is running.
+ let _lock = self
+ .bcx
+ .config
+ .acquire_package_cache_lock(CacheLockMode::Shared)?;
let mut queue = JobQueue::new(self.bcx);
let mut plan = BuildPlan::new();
let build_plan = self.bcx.build_config.build_plan;
diff --git a/src/tools/cargo/src/cargo/core/compiler/custom_build.rs b/src/tools/cargo/src/cargo/core/compiler/custom_build.rs
index 3eeeaa0ee..c921986a8 100644
--- a/src/tools/cargo/src/cargo/core/compiler/custom_build.rs
+++ b/src/tools/cargo/src/cargo/core/compiler/custom_build.rs
@@ -307,6 +307,10 @@ fn build_work(cx: &mut Context<'_, '_>, unit: &Unit) -> CargoResult<Job> {
cmd.env("CARGO_MANIFEST_LINKS", links);
}
+ if let Some(trim_paths) = unit.profile.trim_paths.as_ref() {
+ cmd.env("CARGO_TRIM_PATHS", trim_paths.to_string());
+ }
+
// Be sure to pass along all enabled features for this package, this is the
// last piece of statically known information that we have.
for feat in &unit.features {
@@ -353,6 +357,10 @@ fn build_work(cx: &mut Context<'_, '_>, unit: &Unit) -> CargoResult<Job> {
);
cmd.env_remove("RUSTFLAGS");
+ if cx.bcx.ws.config().extra_verbose() {
+ cmd.display_env_vars();
+ }
+
// Gather the set of native dependencies that this package has along with
// some other variables to close over.
//
@@ -399,10 +407,7 @@ fn build_work(cx: &mut Context<'_, '_>, unit: &Unit) -> CargoResult<Job> {
paths::create_dir_all(&script_out_dir)?;
let nightly_features_allowed = cx.bcx.config.nightly_features_allowed;
- let extra_check_cfg = match cx.bcx.config.cli_unstable().check_cfg {
- Some((_, _, _, output)) => output,
- None => false,
- };
+ let extra_check_cfg = cx.bcx.config.cli_unstable().check_cfg;
let targets: Vec<Target> = unit.pkg.targets().to_vec();
// Need a separate copy for the fresh closure.
let targets_fresh = targets.clone();
@@ -802,7 +807,7 @@ impl BuildOutput {
if extra_check_cfg {
check_cfgs.push(value.to_string());
} else {
- warnings.push(format!("cargo:{} requires -Zcheck-cfg=output flag", key));
+ warnings.push(format!("cargo:{} requires -Zcheck-cfg flag", key));
}
}
"rustc-env" => {
@@ -1122,10 +1127,7 @@ fn prev_build_output(cx: &mut Context<'_, '_>, unit: &Unit) -> (Option<BuildOutp
&unit.pkg.to_string(),
&prev_script_out_dir,
&script_out_dir,
- match cx.bcx.config.cli_unstable().check_cfg {
- Some((_, _, _, output)) => output,
- None => false,
- },
+ cx.bcx.config.cli_unstable().check_cfg,
cx.bcx.config.nightly_features_allowed,
unit.pkg.targets(),
)
diff --git a/src/tools/cargo/src/cargo/core/compiler/future_incompat.rs b/src/tools/cargo/src/cargo/core/compiler/future_incompat.rs
index 907a0f97d..af44940bd 100644
--- a/src/tools/cargo/src/cargo/core/compiler/future_incompat.rs
+++ b/src/tools/cargo/src/cargo/core/compiler/future_incompat.rs
@@ -37,6 +37,7 @@ use crate::core::compiler::BuildContext;
use crate::core::{Dependency, PackageId, Workspace};
use crate::sources::source::QueryKind;
use crate::sources::SourceConfigMap;
+use crate::util::cache_lock::CacheLockMode;
use crate::util::{iter_join, CargoResult};
use anyhow::{bail, format_err, Context};
use serde::{Deserialize, Serialize};
@@ -166,7 +167,7 @@ impl OnDiskReports {
let on_disk = serde_json::to_vec(&self).unwrap();
if let Err(e) = ws
.target_dir()
- .open_rw(
+ .open_rw_exclusive_create(
FUTURE_INCOMPAT_FILE,
ws.config(),
"Future incompatibility report",
@@ -190,7 +191,7 @@ impl OnDiskReports {
/// Loads the on-disk reports.
pub fn load(ws: &Workspace<'_>) -> CargoResult<OnDiskReports> {
- let report_file = match ws.target_dir().open_ro(
+ let report_file = match ws.target_dir().open_ro_shared(
FUTURE_INCOMPAT_FILE,
ws.config(),
"Future incompatible report",
@@ -297,7 +298,10 @@ fn render_report(per_package_reports: &[FutureIncompatReportPackage]) -> BTreeMa
/// This is best-effort - if an error occurs, `None` will be returned.
fn get_updates(ws: &Workspace<'_>, package_ids: &BTreeSet<PackageId>) -> Option<String> {
// This in general ignores all errors since this is opportunistic.
- let _lock = ws.config().acquire_package_cache_lock().ok()?;
+ let _lock = ws
+ .config()
+ .acquire_package_cache_lock(CacheLockMode::DownloadExclusive)
+ .ok()?;
// Create a set of updated registry sources.
let map = SourceConfigMap::new(ws.config()).ok()?;
let mut package_ids: BTreeSet<_> = package_ids
diff --git a/src/tools/cargo/src/cargo/core/compiler/job_queue/mod.rs b/src/tools/cargo/src/cargo/core/compiler/job_queue/mod.rs
index 738c8c267..e39fe184d 100644
--- a/src/tools/cargo/src/cargo/core/compiler/job_queue/mod.rs
+++ b/src/tools/cargo/src/cargo/core/compiler/job_queue/mod.rs
@@ -952,7 +952,10 @@ impl<'cfg> DrainState<'cfg> {
}
for warning in output.warnings.iter() {
- bcx.config.shell().warn(warning)?;
+ let warning_with_package =
+ format!("{}@{}: {}", unit.pkg.name(), unit.pkg.version(), warning);
+
+ bcx.config.shell().warn(warning_with_package)?;
}
if msg.is_some() {
diff --git a/src/tools/cargo/src/cargo/core/compiler/layout.rs b/src/tools/cargo/src/cargo/core/compiler/layout.rs
index d92adffeb..57b65907c 100644
--- a/src/tools/cargo/src/cargo/core/compiler/layout.rs
+++ b/src/tools/cargo/src/cargo/core/compiler/layout.rs
@@ -166,7 +166,7 @@ impl Layout {
// For now we don't do any more finer-grained locking on the artifact
// directory, so just lock the entire thing for the duration of this
// compile.
- let lock = dest.open_rw(".cargo-lock", ws.config(), "build directory")?;
+ let lock = dest.open_rw_exclusive_create(".cargo-lock", ws.config(), "build directory")?;
let root = root.into_path_unlocked();
let dest = dest.into_path_unlocked();
let deps = dest.join("deps");
diff --git a/src/tools/cargo/src/cargo/core/compiler/mod.rs b/src/tools/cargo/src/cargo/core/compiler/mod.rs
index b0f15bd61..ab43e9979 100644
--- a/src/tools/cargo/src/cargo/core/compiler/mod.rs
+++ b/src/tools/cargo/src/cargo/core/compiler/mod.rs
@@ -93,7 +93,8 @@ use crate::core::{Feature, PackageId, Target, Verbosity};
use crate::util::errors::{CargoResult, VerboseError};
use crate::util::interning::InternedString;
use crate::util::machine_message::{self, Message};
-use crate::util::toml::TomlDebugInfo;
+use crate::util::toml::schema::TomlDebugInfo;
+use crate::util::toml::schema::TomlTrimPaths;
use crate::util::{add_path_args, internal, iter_join_onto, profile};
use cargo_util::{paths, ProcessBuilder, ProcessError};
use rustfix::diagnostics::Applicability;
@@ -950,6 +951,7 @@ fn build_base_args(cx: &Context<'_, '_>, cmd: &mut ProcessBuilder, unit: &Unit)
incremental,
strip,
rustflags: profile_rustflags,
+ trim_paths,
..
} = unit.profile.clone();
let test = unit.mode.is_any_test();
@@ -1028,6 +1030,10 @@ fn build_base_args(cx: &Context<'_, '_>, cmd: &mut ProcessBuilder, unit: &Unit)
}
}
+ if let Some(trim_paths) = trim_paths {
+ trim_paths_args(cmd, cx, unit, &trim_paths)?;
+ }
+
cmd.args(unit.pkg.manifest().lint_rustflags());
cmd.args(&profile_rustflags);
if let Some(args) = cx.bcx.extra_args_for(unit) {
@@ -1162,44 +1168,105 @@ fn features_args(unit: &Unit) -> Vec<OsString> {
args
}
+/// Generates the `--remap-path-scope` and `--remap-path-prefix` for [RFC 3127].
+/// See also unstable feature [`-Ztrim-paths`].
+///
+/// [RFC 3127]: https://rust-lang.github.io/rfcs/3127-trim-paths.html
+/// [`-Ztrim-paths`]: https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#profile-trim-paths-option
+fn trim_paths_args(
+ cmd: &mut ProcessBuilder,
+ cx: &Context<'_, '_>,
+ unit: &Unit,
+ trim_paths: &TomlTrimPaths,
+) -> CargoResult<()> {
+ if trim_paths.is_none() {
+ return Ok(());
+ }
+
+ // feature gate was checked during mainfest/config parsing.
+ cmd.arg("-Zunstable-options");
+ cmd.arg(format!("-Zremap-path-scope={trim_paths}"));
+
+ let sysroot_remap = {
+ let sysroot = &cx.bcx.target_data.info(unit.kind).sysroot;
+ let mut remap = OsString::from("--remap-path-prefix=");
+ remap.push(sysroot);
+ remap.push("/lib/rustlib/src/rust"); // See also `detect_sysroot_src_path()`.
+ remap.push("=");
+ remap.push("/rustc/");
+ // This remap logic aligns with rustc:
+ // <https://github.com/rust-lang/rust/blob/c2ef3516/src/bootstrap/src/lib.rs#L1113-L1116>
+ if let Some(commit_hash) = cx.bcx.rustc().commit_hash.as_ref() {
+ remap.push(commit_hash);
+ } else {
+ remap.push(cx.bcx.rustc().version.to_string());
+ }
+ remap
+ };
+ cmd.arg(sysroot_remap);
+
+ let package_remap = {
+ let pkg_root = unit.pkg.root();
+ let ws_root = cx.bcx.ws.root();
+ let is_local = unit.pkg.package_id().source_id().is_path();
+ let mut remap = OsString::from("--remap-path-prefix=");
+ // Remapped to path relative to workspace root:
+ //
+ // * path dependencies under workspace root directory
+ //
+ // Remapped to `<pkg>-<version>`
+ //
+ // * registry dependencies
+ // * git dependencies
+ // * path dependencies outside workspace root directory
+ if is_local && pkg_root.strip_prefix(ws_root).is_ok() {
+ remap.push(ws_root);
+ remap.push("="); // empty to remap to relative paths.
+ } else {
+ remap.push(pkg_root);
+ remap.push("=");
+ remap.push(unit.pkg.name());
+ remap.push("-");
+ remap.push(unit.pkg.version().to_string());
+ }
+ remap
+ };
+ cmd.arg(package_remap);
+
+ Ok(())
+}
+
/// Generates the `--check-cfg` arguments for the `unit`.
/// See unstable feature [`check-cfg`].
///
/// [`check-cfg`]: https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#check-cfg
fn check_cfg_args(cx: &Context<'_, '_>, unit: &Unit) -> Vec<OsString> {
- if let Some((features, well_known_names, well_known_values, _output)) =
- cx.bcx.config.cli_unstable().check_cfg
- {
- let mut args = Vec::with_capacity(unit.pkg.summary().features().len() * 2 + 4);
- args.push(OsString::from("-Zunstable-options"));
-
- if features {
- // This generate something like this:
- // - values(feature)
- // - values(feature, "foo", "bar")
- let mut arg = OsString::from("values(feature");
- for (&feat, _) in unit.pkg.summary().features() {
- arg.push(", \"");
- arg.push(&feat);
- arg.push("\"");
+ if cx.bcx.config.cli_unstable().check_cfg {
+ // This generate something like this:
+ // - cfg(feature, values())
+ // - cfg(feature, values("foo", "bar"))
+ //
+ // NOTE: Despite only explicitly specifying `feature`, well known names and values
+ // are implicitly enabled when one or more `--check-cfg` argument is passed.
+
+ let gross_cap_estimation = unit.pkg.summary().features().len() * 7 + 25;
+ let mut arg_feature = OsString::with_capacity(gross_cap_estimation);
+ arg_feature.push("cfg(feature, values(");
+ for (i, feature) in unit.pkg.summary().features().keys().enumerate() {
+ if i != 0 {
+ arg_feature.push(", ");
}
- arg.push(")");
-
- args.push(OsString::from("--check-cfg"));
- args.push(arg);
- }
-
- if well_known_names {
- args.push(OsString::from("--check-cfg"));
- args.push(OsString::from("names()"));
- }
-
- if well_known_values {
- args.push(OsString::from("--check-cfg"));
- args.push(OsString::from("values()"));
+ arg_feature.push("\"");
+ arg_feature.push(feature);
+ arg_feature.push("\"");
}
+ arg_feature.push("))");
- args
+ vec![
+ OsString::from("-Zunstable-options"),
+ OsString::from("--check-cfg"),
+ arg_feature,
+ ]
} else {
Vec::new()
}
diff --git a/src/tools/cargo/src/cargo/core/compiler/timings.rs b/src/tools/cargo/src/cargo/core/compiler/timings.rs
index 7c388bd10..98c36cfdc 100644
--- a/src/tools/cargo/src/cargo/core/compiler/timings.rs
+++ b/src/tools/cargo/src/cargo/core/compiler/timings.rs
@@ -339,18 +339,21 @@ impl<'cfg> Timings<'cfg> {
include_str!("timings.js")
)?;
drop(f);
- let msg = format!(
- "report saved to {}",
- std::env::current_dir()
- .unwrap_or_default()
- .join(&filename)
- .display()
- );
+
let unstamped_filename = timings_path.join("cargo-timing.html");
paths::link_or_copy(&filename, &unstamped_filename)?;
- self.config
- .shell()
- .status_with_color("Timing", msg, &style::NOTE)?;
+
+ let mut shell = self.config.shell();
+ let timing_path = std::env::current_dir().unwrap_or_default().join(&filename);
+ let link = shell.err_file_hyperlink(&timing_path);
+ let msg = format!(
+ "report saved to {}{}{}",
+ link.open(),
+ timing_path.display(),
+ link.close()
+ );
+ shell.status_with_color("Timing", msg, &style::NOTE)?;
+
Ok(())
}
@@ -380,14 +383,7 @@ impl<'cfg> Timings<'cfg> {
.unwrap_or_else(|_| "n/a".into());
let rustc_info = render_rustc_info(bcx);
let error_msg = match error {
- Some(e) => format!(
- r#"\
- <tr>
- <td class="error-text">Error:</td><td>{}</td>
- </tr>
-"#,
- e
- ),
+ Some(e) => format!(r#"<tr><td class="error-text">Error:</td><td>{e}</td></tr>"#),
None => "".to_string(),
};
write!(
diff --git a/src/tools/cargo/src/cargo/core/dependency.rs b/src/tools/cargo/src/cargo/core/dependency.rs
index f00bb0590..fe102842a 100644
--- a/src/tools/cargo/src/cargo/core/dependency.rs
+++ b/src/tools/cargo/src/cargo/core/dependency.rs
@@ -352,9 +352,7 @@ impl Dependency {
// Only update the `precise` of this source to preserve other
// information about dependency's source which may not otherwise be
// tested during equality/hashing.
- me.source_id = me
- .source_id
- .with_precise(id.source_id().precise().map(|s| s.to_string()));
+ me.source_id = me.source_id.with_precise_from(id.source_id());
self
}
diff --git a/src/tools/cargo/src/cargo/core/features.rs b/src/tools/cargo/src/cargo/core/features.rs
index 5faa2087e..72a267f04 100644
--- a/src/tools/cargo/src/cargo/core/features.rs
+++ b/src/tools/cargo/src/cargo/core/features.rs
@@ -195,7 +195,7 @@ pub const SEE_CHANNELS: &str =
/// [`LATEST_STABLE`]: Edition::LATEST_STABLE
/// [this example]: https://github.com/rust-lang/cargo/blob/3ebb5f15a940810f250b68821149387af583a79e/src/doc/src/reference/unstable.md?plain=1#L1238-L1264
/// [`is_stable`]: Edition::is_stable
-/// [`TomlManifest::to_real_manifest`]: crate::util::toml::TomlManifest::to_real_manifest
+/// [`TomlManifest::to_real_manifest`]: crate::util::toml::schema::TomlManifest::to_real_manifest
/// [`features!`]: macro.features.html
#[derive(Clone, Copy, Debug, Hash, PartialOrd, Ord, Eq, PartialEq, Serialize, Deserialize)]
pub enum Edition {
@@ -205,20 +205,22 @@ pub enum Edition {
Edition2018,
/// The 2021 edition
Edition2021,
+ /// The 2024 edition
+ Edition2024,
}
impl Edition {
/// The latest edition that is unstable.
///
/// This is `None` if there is no next unstable edition.
- pub const LATEST_UNSTABLE: Option<Edition> = None;
+ pub const LATEST_UNSTABLE: Option<Edition> = Some(Edition::Edition2024);
/// The latest stable edition.
pub const LATEST_STABLE: Edition = Edition::Edition2021;
/// Possible values allowed for the `--edition` CLI flag.
///
/// This requires a static value due to the way clap works, otherwise I
/// would have built this dynamically.
- pub const CLI_VALUES: [&'static str; 3] = ["2015", "2018", "2021"];
+ pub const CLI_VALUES: [&'static str; 4] = ["2015", "2018", "2021", "2024"];
/// Returns the first version that a particular edition was released on
/// stable.
@@ -228,6 +230,7 @@ impl Edition {
Edition2015 => None,
Edition2018 => Some(semver::Version::new(1, 31, 0)),
Edition2021 => Some(semver::Version::new(1, 56, 0)),
+ Edition2024 => None,
}
}
@@ -238,6 +241,7 @@ impl Edition {
Edition2015 => true,
Edition2018 => true,
Edition2021 => true,
+ Edition2024 => false,
}
}
@@ -250,6 +254,7 @@ impl Edition {
Edition2015 => None,
Edition2018 => Some(Edition2015),
Edition2021 => Some(Edition2018),
+ Edition2024 => Some(Edition2021),
}
}
@@ -260,7 +265,8 @@ impl Edition {
match self {
Edition2015 => Edition2018,
Edition2018 => Edition2021,
- Edition2021 => Edition2021,
+ Edition2021 => Edition2024,
+ Edition2024 => Edition2024,
}
}
@@ -286,6 +292,7 @@ impl Edition {
Edition2015 => false,
Edition2018 => true,
Edition2021 => true,
+ Edition2024 => false,
}
}
@@ -298,6 +305,7 @@ impl Edition {
Edition2015 => false,
Edition2018 => true,
Edition2021 => false,
+ Edition2024 => false,
}
}
@@ -316,6 +324,7 @@ impl fmt::Display for Edition {
Edition::Edition2015 => f.write_str("2015"),
Edition::Edition2018 => f.write_str("2018"),
Edition::Edition2021 => f.write_str("2021"),
+ Edition::Edition2024 => f.write_str("2024"),
}
}
}
@@ -326,13 +335,14 @@ impl FromStr for Edition {
"2015" => Ok(Edition::Edition2015),
"2018" => Ok(Edition::Edition2018),
"2021" => Ok(Edition::Edition2021),
- s if s.parse().map_or(false, |y: u16| y > 2021 && y < 2050) => bail!(
+ "2024" => Ok(Edition::Edition2024),
+ s if s.parse().map_or(false, |y: u16| y > 2024 && y < 2050) => bail!(
"this version of Cargo is older than the `{}` edition, \
- and only supports `2015`, `2018`, and `2021` editions.",
+ and only supports `2015`, `2018`, `2021`, and `2024` editions.",
s
),
s => bail!(
- "supported edition values are `2015`, `2018`, or `2021`, \
+ "supported edition values are `2015`, `2018`, `2021`, or `2024`, \
but `{}` is unknown",
s
),
@@ -483,6 +493,12 @@ features! {
// Allow specifying rustflags directly in a profile
(stable, workspace_inheritance, "1.64", "reference/unstable.html#workspace-inheritance"),
+
+ // Support for 2024 edition.
+ (unstable, edition2024, "", "reference/unstable.html#edition-2024"),
+
+ // Allow setting trim-paths in a profile to control the sanitisation of file paths in build outputs.
+ (unstable, trim_paths, "", "reference/unstable.html#profile-trim-paths-option"),
}
pub struct Feature {
@@ -718,8 +734,7 @@ unstable_cli_options!(
#[serde(deserialize_with = "deserialize_build_std")]
build_std: Option<Vec<String>> = ("Enable Cargo to compile the standard library itself as part of a crate graph compilation"),
build_std_features: Option<Vec<String>> = ("Configure features enabled for the standard library itself when building the standard library"),
- #[serde(deserialize_with = "deserialize_check_cfg")]
- check_cfg: Option<(/*features:*/ bool, /*well_known_names:*/ bool, /*well_known_values:*/ bool, /*output:*/ bool)> = ("Specify scope of compile-time checking of `cfg` names/values"),
+ check_cfg: bool = ("Enable compile-time checking of `cfg` names/values/features"),
codegen_backend: bool = ("Enable the `codegen-backend` option in profiles in .cargo/config.toml file"),
config_include: bool = ("Enable the `include` key in config files"),
direct_minimal_versions: bool = ("Resolve minimal dependency versions instead of maximum (direct dependencies only)"),
@@ -743,6 +758,7 @@ unstable_cli_options!(
separate_nightlies: bool = (HIDDEN),
skip_rustdoc_fingerprint: bool = (HIDDEN),
target_applies_to_host: bool = ("Enable the `target-applies-to-host` key in the .cargo/config.toml file"),
+ trim_paths: bool = ("Enable the `trim-paths` option in profiles"),
unstable_options: bool = ("Allow the usage of unstable options"),
);
@@ -829,20 +845,6 @@ where
))
}
-fn deserialize_check_cfg<'de, D>(
- deserializer: D,
-) -> Result<Option<(bool, bool, bool, bool)>, D::Error>
-where
- D: serde::Deserializer<'de>,
-{
- use serde::de::Error;
- let Some(crates) = <Option<Vec<String>>>::deserialize(deserializer)? else {
- return Ok(None);
- };
-
- parse_check_cfg(crates.into_iter()).map_err(D::Error::custom)
-}
-
#[derive(Debug, Copy, Clone, Default, Deserialize)]
pub struct GitoxideFeatures {
/// All fetches are done with `gitoxide`, which includes git dependencies as well as the crates index.
@@ -911,32 +913,6 @@ fn parse_gitoxide(
Ok(Some(out))
}
-fn parse_check_cfg(
- it: impl Iterator<Item = impl AsRef<str>>,
-) -> CargoResult<Option<(bool, bool, bool, bool)>> {
- let mut features = false;
- let mut well_known_names = false;
- let mut well_known_values = false;
- let mut output = false;
-
- for e in it {
- match e.as_ref() {
- "features" => features = true,
- "names" => well_known_names = true,
- "values" => well_known_values = true,
- "output" => output = true,
- _ => bail!("unstable check-cfg only takes `features`, `names`, `values` or `output` as valid inputs"),
- }
- }
-
- Ok(Some((
- features,
- well_known_names,
- well_known_values,
- output,
- )))
-}
-
impl CliUnstable {
pub fn parse(
&mut self,
@@ -1094,7 +1070,7 @@ impl CliUnstable {
}
"build-std-features" => self.build_std_features = Some(parse_features(v)),
"check-cfg" => {
- self.check_cfg = v.map_or(Ok(None), |v| parse_check_cfg(v.split(',')))?
+ self.check_cfg = parse_empty(k, v)?;
}
"codegen-backend" => self.codegen_backend = parse_empty(k, v)?,
"config-include" => self.config_include = parse_empty(k, v)?,
@@ -1117,6 +1093,7 @@ impl CliUnstable {
"no-index-update" => self.no_index_update = parse_empty(k, v)?,
"panic-abort-tests" => self.panic_abort_tests = parse_empty(k, v)?,
"profile-rustflags" => self.profile_rustflags = parse_empty(k, v)?,
+ "trim-paths" => self.trim_paths = parse_empty(k, v)?,
"publish-timeout" => self.publish_timeout = parse_empty(k, v)?,
"rustdoc-map" => self.rustdoc_map = parse_empty(k, v)?,
"rustdoc-scrape-examples" => self.rustdoc_scrape_examples = parse_empty(k, v)?,
@@ -1125,7 +1102,10 @@ impl CliUnstable {
"script" => self.script = parse_empty(k, v)?,
"target-applies-to-host" => self.target_applies_to_host = parse_empty(k, v)?,
"unstable-options" => self.unstable_options = parse_empty(k, v)?,
- _ => bail!("unknown `-Z` flag specified: {}", k),
+ _ => bail!("\
+ unknown `-Z` flag specified: {k}\n\n\
+ For available unstable features, see https://doc.rust-lang.org/nightly/cargo/reference/unstable.html\n\
+ If you intended to use an unstable rustc feature, try setting `RUSTFLAGS=\"-Z{k}\"`"),
}
Ok(())
diff --git a/src/tools/cargo/src/cargo/core/manifest.rs b/src/tools/cargo/src/cargo/core/manifest.rs
index 7886abec3..66af40c10 100644
--- a/src/tools/cargo/src/cargo/core/manifest.rs
+++ b/src/tools/cargo/src/cargo/core/manifest.rs
@@ -18,7 +18,7 @@ use crate::core::{Dependency, PackageId, PackageIdSpec, SourceId, Summary};
use crate::core::{Edition, Feature, Features, WorkspaceConfig};
use crate::util::errors::*;
use crate::util::interning::InternedString;
-use crate::util::toml::{TomlManifest, TomlProfiles};
+use crate::util::toml::schema::{TomlManifest, TomlProfiles};
use crate::util::{short_hash, Config, Filesystem, RustVersion};
pub enum EitherManifest {
diff --git a/src/tools/cargo/src/cargo/core/mod.rs b/src/tools/cargo/src/cargo/core/mod.rs
index 9b56564a7..2add52d5c 100644
--- a/src/tools/cargo/src/cargo/core/mod.rs
+++ b/src/tools/cargo/src/cargo/core/mod.rs
@@ -14,7 +14,7 @@ pub use self::workspace::{
find_workspace_root, resolve_relative_path, MaybePackage, Workspace, WorkspaceConfig,
WorkspaceRootConfig,
};
-pub use crate::util::toml::InheritableFields;
+pub use crate::util::toml::schema::InheritableFields;
pub mod compiler;
pub mod dependency;
diff --git a/src/tools/cargo/src/cargo/core/package.rs b/src/tools/cargo/src/cargo/core/package.rs
index 76f6c405b..274798474 100644
--- a/src/tools/cargo/src/cargo/core/package.rs
+++ b/src/tools/cargo/src/cargo/core/package.rs
@@ -24,7 +24,7 @@ use crate::core::resolver::{HasDevUnits, Resolve};
use crate::core::{Dependency, Manifest, PackageId, SourceId, Target};
use crate::core::{Summary, Workspace};
use crate::sources::source::{MaybePackage, SourceMap};
-use crate::util::config::PackageCacheLock;
+use crate::util::cache_lock::{CacheLock, CacheLockMode};
use crate::util::errors::{CargoResult, HttpNotSuccessful};
use crate::util::interning::InternedString;
use crate::util::network::http::http_handle_and_timeout;
@@ -367,7 +367,7 @@ pub struct Downloads<'a, 'cfg> {
next_speed_check_bytes_threshold: Cell<u64>,
/// Global filesystem lock to ensure only one Cargo is downloading at a
/// time.
- _lock: PackageCacheLock<'cfg>,
+ _lock: CacheLock<'cfg>,
}
struct Download<'cfg> {
@@ -465,7 +465,9 @@ impl<'cfg> PackageSet<'cfg> {
timeout,
next_speed_check: Cell::new(Instant::now()),
next_speed_check_bytes_threshold: Cell::new(0),
- _lock: self.config.acquire_package_cache_lock()?,
+ _lock: self
+ .config
+ .acquire_package_cache_lock(CacheLockMode::DownloadExclusive)?,
})
}
@@ -478,6 +480,9 @@ impl<'cfg> PackageSet<'cfg> {
pub fn get_many(&self, ids: impl IntoIterator<Item = PackageId>) -> CargoResult<Vec<&Package>> {
let mut pkgs = Vec::new();
+ let _lock = self
+ .config
+ .acquire_package_cache_lock(CacheLockMode::DownloadExclusive)?;
let mut downloads = self.enable_download()?;
for id in ids {
pkgs.extend(downloads.start(id)?);
diff --git a/src/tools/cargo/src/cargo/core/package_id.rs b/src/tools/cargo/src/cargo/core/package_id.rs
index ca126172c..3e9c03a47 100644
--- a/src/tools/cargo/src/cargo/core/package_id.rs
+++ b/src/tools/cargo/src/cargo/core/package_id.rs
@@ -12,7 +12,7 @@ use serde::ser;
use crate::core::SourceId;
use crate::util::interning::InternedString;
-use crate::util::{CargoResult, ToSemver};
+use crate::util::CargoResult;
static PACKAGE_ID_CACHE: OnceLock<Mutex<HashSet<&'static PackageIdInner>>> = OnceLock::new();
@@ -29,8 +29,7 @@ struct PackageIdInner {
source_id: SourceId,
}
-// Custom equality that uses full equality of SourceId, rather than its custom equality,
-// and Version, which usually ignores `build` metadata.
+// Custom equality that uses full equality of SourceId, rather than its custom equality.
//
// The `build` part of the version is usually ignored (like a "comment").
// However, there are some cases where it is important. The download path from
@@ -40,11 +39,7 @@ struct PackageIdInner {
impl PartialEq for PackageIdInner {
fn eq(&self, other: &Self) -> bool {
self.name == other.name
- && self.version.major == other.version.major
- && self.version.minor == other.version.minor
- && self.version.patch == other.version.patch
- && self.version.pre == other.version.pre
- && self.version.build == other.version.build
+ && self.version == other.version
&& self.source_id.full_eq(other.source_id)
}
}
@@ -53,11 +48,7 @@ impl PartialEq for PackageIdInner {
impl Hash for PackageIdInner {
fn hash<S: hash::Hasher>(&self, into: &mut S) {
self.name.hash(into);
- self.version.major.hash(into);
- self.version.minor.hash(into);
- self.version.patch.hash(into);
- self.version.pre.hash(into);
- self.version.build.hash(into);
+ self.version.hash(into);
self.source_id.full_hash(into);
}
}
@@ -82,27 +73,31 @@ impl<'de> de::Deserialize<'de> for PackageId {
D: de::Deserializer<'de>,
{
let string = String::deserialize(d)?;
- let mut s = string.splitn(3, ' ');
- let name = s.next().unwrap();
- let name = InternedString::new(name);
- let Some(version) = s.next() else {
- return Err(de::Error::custom("invalid serialized PackageId"));
- };
- let version = version.to_semver().map_err(de::Error::custom)?;
- let Some(url) = s.next() else {
- return Err(de::Error::custom("invalid serialized PackageId"));
- };
- let url = if url.starts_with('(') && url.ends_with(')') {
- &url[1..url.len() - 1]
- } else {
- return Err(de::Error::custom("invalid serialized PackageId"));
- };
+
+ let (field, rest) = string
+ .split_once(' ')
+ .ok_or_else(|| de::Error::custom("invalid serialized PackageId"))?;
+ let name = InternedString::new(field);
+
+ let (field, rest) = rest
+ .split_once(' ')
+ .ok_or_else(|| de::Error::custom("invalid serialized PackageId"))?;
+ let version = field.parse().map_err(de::Error::custom)?;
+
+ let url =
+ strip_parens(rest).ok_or_else(|| de::Error::custom("invalid serialized PackageId"))?;
let source_id = SourceId::from_url(url).map_err(de::Error::custom)?;
Ok(PackageId::pure(name, version, source_id))
}
}
+fn strip_parens(value: &str) -> Option<&str> {
+ let value = value.strip_prefix('(')?;
+ let value = value.strip_suffix(')')?;
+ Some(value)
+}
+
impl PartialEq for PackageId {
fn eq(&self, other: &PackageId) -> bool {
if ptr::eq(self.inner, other.inner) {
@@ -128,12 +123,12 @@ impl Hash for PackageId {
}
impl PackageId {
- pub fn new<T: ToSemver>(
+ pub fn new(
name: impl Into<InternedString>,
- version: T,
+ version: &str,
sid: SourceId,
) -> CargoResult<PackageId> {
- let v = version.to_semver()?;
+ let v = version.parse()?;
Ok(PackageId::pure(name.into(), v, sid))
}
@@ -165,14 +160,6 @@ impl PackageId {
self.inner.source_id
}
- pub fn with_precise(self, precise: Option<String>) -> PackageId {
- PackageId::pure(
- self.inner.name,
- self.inner.version.clone(),
- self.inner.source_id.with_precise(precise),
- )
- }
-
pub fn with_source_id(self, source: SourceId) -> PackageId {
PackageId::pure(self.inner.name, self.inner.version.clone(), source)
}
@@ -233,6 +220,16 @@ impl fmt::Debug for PackageId {
}
}
+impl fmt::Debug for PackageIdInner {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.debug_struct("PackageIdInner")
+ .field("name", &self.name)
+ .field("version", &self.version.to_string())
+ .field("source", &self.source_id.to_string())
+ .finish()
+ }
+}
+
#[cfg(test)]
mod tests {
use super::PackageId;
@@ -257,4 +254,14 @@ mod tests {
let pkg_id = PackageId::new("foo", "1.0.0", SourceId::for_registry(&loc).unwrap()).unwrap();
assert_eq!("foo v1.0.0", pkg_id.to_string());
}
+
+ #[test]
+ fn unequal_build_metadata() {
+ let loc = CRATES_IO_INDEX.into_url().unwrap();
+ let repo = SourceId::for_registry(&loc).unwrap();
+ let first = PackageId::new("foo", "0.0.1+first", repo).unwrap();
+ let second = PackageId::new("foo", "0.0.1+second", repo).unwrap();
+ assert_ne!(first, second);
+ assert_ne!(first.inner, second.inner);
+ }
}
diff --git a/src/tools/cargo/src/cargo/core/package_id_spec.rs b/src/tools/cargo/src/cargo/core/package_id_spec.rs
index 53d99b84b..c617c1f7a 100644
--- a/src/tools/cargo/src/cargo/core/package_id_spec.rs
+++ b/src/tools/cargo/src/cargo/core/package_id_spec.rs
@@ -9,9 +9,8 @@ use url::Url;
use crate::core::PackageId;
use crate::util::edit_distance;
use crate::util::errors::CargoResult;
-use crate::util::interning::InternedString;
-use crate::util::PartialVersion;
use crate::util::{validate_package_name, IntoUrl};
+use crate::util_semver::PartialVersion;
/// Some or all of the data required to identify a package:
///
@@ -24,7 +23,7 @@ use crate::util::{validate_package_name, IntoUrl};
/// sufficient to uniquely define a package ID.
#[derive(Clone, PartialEq, Eq, Debug, Hash, Ord, PartialOrd)]
pub struct PackageIdSpec {
- name: InternedString,
+ name: String,
version: Option<PartialVersion>,
url: Option<Url>,
}
@@ -76,7 +75,7 @@ impl PackageIdSpec {
};
validate_package_name(name, "pkgid", "")?;
Ok(PackageIdSpec {
- name: InternedString::new(name),
+ name: String::from(name),
version,
url: None,
})
@@ -99,7 +98,7 @@ impl PackageIdSpec {
/// fields filled in.
pub fn from_package_id(package_id: PackageId) -> PackageIdSpec {
PackageIdSpec {
- name: package_id.name(),
+ name: String::from(package_id.name().as_str()),
version: Some(package_id.version().clone().into()),
url: Some(package_id.source_id().url().clone()),
}
@@ -127,18 +126,18 @@ impl PackageIdSpec {
Some(fragment) => match fragment.split_once([':', '@']) {
Some((name, part)) => {
let version = part.parse::<PartialVersion>()?;
- (InternedString::new(name), Some(version))
+ (String::from(name), Some(version))
}
None => {
if fragment.chars().next().unwrap().is_alphabetic() {
- (InternedString::new(&fragment), None)
+ (String::from(fragment.as_str()), None)
} else {
let version = fragment.parse::<PartialVersion>()?;
- (InternedString::new(path_name), Some(version))
+ (String::from(path_name), Some(version))
}
}
},
- None => (InternedString::new(path_name), None),
+ None => (String::from(path_name), None),
}
};
Ok(PackageIdSpec {
@@ -148,13 +147,13 @@ impl PackageIdSpec {
})
}
- pub fn name(&self) -> InternedString {
- self.name
+ pub fn name(&self) -> &str {
+ self.name.as_str()
}
/// Full `semver::Version`, if present
pub fn version(&self) -> Option<Version> {
- self.version.as_ref().and_then(|v| v.version())
+ self.version.as_ref().and_then(|v| v.to_version())
}
pub fn partial_version(&self) -> Option<&PartialVersion> {
@@ -171,7 +170,7 @@ impl PackageIdSpec {
/// Checks whether the given `PackageId` matches the `PackageIdSpec`.
pub fn matches(&self, package_id: PackageId) -> bool {
- if self.name() != package_id.name() {
+ if self.name() != package_id.name().as_str() {
return false;
}
@@ -181,10 +180,13 @@ impl PackageIdSpec {
}
}
- match self.url {
- Some(ref u) => u == package_id.source_id().url(),
- None => true,
+ if let Some(u) = &self.url {
+ if u != package_id.source_id().url() {
+ return false;
+ }
}
+
+ true
}
/// Checks a list of `PackageId`s to find 1 that matches this `PackageIdSpec`. If 0, 2, or
@@ -211,7 +213,7 @@ impl PackageIdSpec {
if self.url.is_some() {
try_spec(
PackageIdSpec {
- name: self.name,
+ name: self.name.clone(),
version: self.version.clone(),
url: None,
},
@@ -221,7 +223,7 @@ impl PackageIdSpec {
if suggestion.is_empty() && self.version.is_some() {
try_spec(
PackageIdSpec {
- name: self.name,
+ name: self.name.clone(),
version: None,
url: None,
},
@@ -324,7 +326,6 @@ impl<'de> de::Deserialize<'de> for PackageIdSpec {
mod tests {
use super::PackageIdSpec;
use crate::core::{PackageId, SourceId};
- use crate::util::interning::InternedString;
use url::Url;
#[test]
@@ -333,13 +334,16 @@ mod tests {
fn ok(spec: &str, expected: PackageIdSpec, expected_rendered: &str) {
let parsed = PackageIdSpec::parse(spec).unwrap();
assert_eq!(parsed, expected);
- assert_eq!(parsed.to_string(), expected_rendered);
+ let rendered = parsed.to_string();
+ assert_eq!(rendered, expected_rendered);
+ let reparsed = PackageIdSpec::parse(&rendered).unwrap();
+ assert_eq!(reparsed, expected);
}
ok(
"https://crates.io/foo",
PackageIdSpec {
- name: InternedString::new("foo"),
+ name: String::from("foo"),
version: None,
url: Some(Url::parse("https://crates.io/foo").unwrap()),
},
@@ -348,7 +352,7 @@ mod tests {
ok(
"https://crates.io/foo#1.2.3",
PackageIdSpec {
- name: InternedString::new("foo"),
+ name: String::from("foo"),
version: Some("1.2.3".parse().unwrap()),
url: Some(Url::parse("https://crates.io/foo").unwrap()),
},
@@ -357,7 +361,7 @@ mod tests {
ok(
"https://crates.io/foo#1.2",
PackageIdSpec {
- name: InternedString::new("foo"),
+ name: String::from("foo"),
version: Some("1.2".parse().unwrap()),
url: Some(Url::parse("https://crates.io/foo").unwrap()),
},
@@ -366,7 +370,7 @@ mod tests {
ok(
"https://crates.io/foo#bar:1.2.3",
PackageIdSpec {
- name: InternedString::new("bar"),
+ name: String::from("bar"),
version: Some("1.2.3".parse().unwrap()),
url: Some(Url::parse("https://crates.io/foo").unwrap()),
},
@@ -375,7 +379,7 @@ mod tests {
ok(
"https://crates.io/foo#bar@1.2.3",
PackageIdSpec {
- name: InternedString::new("bar"),
+ name: String::from("bar"),
version: Some("1.2.3".parse().unwrap()),
url: Some(Url::parse("https://crates.io/foo").unwrap()),
},
@@ -384,7 +388,7 @@ mod tests {
ok(
"https://crates.io/foo#bar@1.2",
PackageIdSpec {
- name: InternedString::new("bar"),
+ name: String::from("bar"),
version: Some("1.2".parse().unwrap()),
url: Some(Url::parse("https://crates.io/foo").unwrap()),
},
@@ -393,7 +397,7 @@ mod tests {
ok(
"foo",
PackageIdSpec {
- name: InternedString::new("foo"),
+ name: String::from("foo"),
version: None,
url: None,
},
@@ -402,7 +406,7 @@ mod tests {
ok(
"foo:1.2.3",
PackageIdSpec {
- name: InternedString::new("foo"),
+ name: String::from("foo"),
version: Some("1.2.3".parse().unwrap()),
url: None,
},
@@ -411,7 +415,7 @@ mod tests {
ok(
"foo@1.2.3",
PackageIdSpec {
- name: InternedString::new("foo"),
+ name: String::from("foo"),
version: Some("1.2.3".parse().unwrap()),
url: None,
},
@@ -420,12 +424,104 @@ mod tests {
ok(
"foo@1.2",
PackageIdSpec {
- name: InternedString::new("foo"),
+ name: String::from("foo"),
version: Some("1.2".parse().unwrap()),
url: None,
},
"foo@1.2",
);
+
+ // pkgid-spec.md
+ ok(
+ "regex",
+ PackageIdSpec {
+ name: String::from("regex"),
+ version: None,
+ url: None,
+ },
+ "regex",
+ );
+ ok(
+ "regex@1.4",
+ PackageIdSpec {
+ name: String::from("regex"),
+ version: Some("1.4".parse().unwrap()),
+ url: None,
+ },
+ "regex@1.4",
+ );
+ ok(
+ "regex@1.4.3",
+ PackageIdSpec {
+ name: String::from("regex"),
+ version: Some("1.4.3".parse().unwrap()),
+ url: None,
+ },
+ "regex@1.4.3",
+ );
+ ok(
+ "https://github.com/rust-lang/crates.io-index#regex",
+ PackageIdSpec {
+ name: String::from("regex"),
+ version: None,
+ url: Some(Url::parse("https://github.com/rust-lang/crates.io-index").unwrap()),
+ },
+ "https://github.com/rust-lang/crates.io-index#regex",
+ );
+ ok(
+ "https://github.com/rust-lang/crates.io-index#regex@1.4.3",
+ PackageIdSpec {
+ name: String::from("regex"),
+ version: Some("1.4.3".parse().unwrap()),
+ url: Some(Url::parse("https://github.com/rust-lang/crates.io-index").unwrap()),
+ },
+ "https://github.com/rust-lang/crates.io-index#regex@1.4.3",
+ );
+ ok(
+ "https://github.com/rust-lang/cargo#0.52.0",
+ PackageIdSpec {
+ name: String::from("cargo"),
+ version: Some("0.52.0".parse().unwrap()),
+ url: Some(Url::parse("https://github.com/rust-lang/cargo").unwrap()),
+ },
+ "https://github.com/rust-lang/cargo#0.52.0",
+ );
+ ok(
+ "https://github.com/rust-lang/cargo#cargo-platform@0.1.2",
+ PackageIdSpec {
+ name: String::from("cargo-platform"),
+ version: Some("0.1.2".parse().unwrap()),
+ url: Some(Url::parse("https://github.com/rust-lang/cargo").unwrap()),
+ },
+ "https://github.com/rust-lang/cargo#cargo-platform@0.1.2",
+ );
+ ok(
+ "ssh://git@github.com/rust-lang/regex.git#regex@1.4.3",
+ PackageIdSpec {
+ name: String::from("regex"),
+ version: Some("1.4.3".parse().unwrap()),
+ url: Some(Url::parse("ssh://git@github.com/rust-lang/regex.git").unwrap()),
+ },
+ "ssh://git@github.com/rust-lang/regex.git#regex@1.4.3",
+ );
+ ok(
+ "file:///path/to/my/project/foo",
+ PackageIdSpec {
+ name: String::from("foo"),
+ version: None,
+ url: Some(Url::parse("file:///path/to/my/project/foo").unwrap()),
+ },
+ "file:///path/to/my/project/foo",
+ );
+ ok(
+ "file:///path/to/my/project/foo#1.1.8",
+ PackageIdSpec {
+ name: String::from("foo"),
+ version: Some("1.1.8".parse().unwrap()),
+ url: Some(Url::parse("file:///path/to/my/project/foo").unwrap()),
+ },
+ "file:///path/to/my/project/foo#1.1.8",
+ );
}
#[test]
@@ -452,6 +548,12 @@ mod tests {
assert!(PackageIdSpec::parse("foo@1.2.3").unwrap().matches(foo));
assert!(!PackageIdSpec::parse("foo@1.2.2").unwrap().matches(foo));
assert!(PackageIdSpec::parse("foo@1.2").unwrap().matches(foo));
+ assert!(PackageIdSpec::parse("https://example.com#foo@1.2")
+ .unwrap()
+ .matches(foo));
+ assert!(!PackageIdSpec::parse("https://bob.com#foo@1.2")
+ .unwrap()
+ .matches(foo));
let meta = PackageId::new("meta", "1.2.3+hello", sid).unwrap();
assert!(PackageIdSpec::parse("meta").unwrap().matches(meta));
diff --git a/src/tools/cargo/src/cargo/core/profiles.rs b/src/tools/cargo/src/cargo/core/profiles.rs
index 1ad9ed5f7..ec53dbae5 100644
--- a/src/tools/cargo/src/cargo/core/profiles.rs
+++ b/src/tools/cargo/src/cargo/core/profiles.rs
@@ -24,9 +24,12 @@
use crate::core::compiler::{CompileKind, CompileTarget, Unit};
use crate::core::dependency::Artifact;
use crate::core::resolver::features::FeaturesFor;
+use crate::core::Feature;
use crate::core::{PackageId, PackageIdSpec, Resolve, Shell, Target, Workspace};
use crate::util::interning::InternedString;
-use crate::util::toml::{
+use crate::util::toml::schema::TomlTrimPaths;
+use crate::util::toml::schema::TomlTrimPathsValue;
+use crate::util::toml::schema::{
ProfilePackageSpec, StringOrBool, TomlDebugInfo, TomlProfile, TomlProfiles,
};
use crate::util::{closest_msg, config, CargoResult, Config};
@@ -80,7 +83,9 @@ impl Profiles {
rustc_host,
};
- Self::add_root_profiles(&mut profile_makers, &profiles);
+ let trim_paths_enabled = ws.unstable_features().is_enabled(Feature::trim_paths())
+ || config.cli_unstable().trim_paths;
+ Self::add_root_profiles(&mut profile_makers, &profiles, trim_paths_enabled);
// Merge with predefined profiles.
use std::collections::btree_map::Entry;
@@ -104,7 +109,7 @@ impl Profiles {
// Verify that the requested profile is defined *somewhere*.
// This simplifies the API (no need for CargoResult), and enforces
// assumptions about how config profiles are loaded.
- profile_makers.get_profile_maker(requested_profile)?;
+ profile_makers.get_profile_maker(&requested_profile)?;
Ok(profile_makers)
}
@@ -123,6 +128,7 @@ impl Profiles {
fn add_root_profiles(
profile_makers: &mut Profiles,
profiles: &BTreeMap<InternedString, TomlProfile>,
+ trim_paths_enabled: bool,
) {
profile_makers.by_name.insert(
InternedString::new("dev"),
@@ -131,7 +137,10 @@ impl Profiles {
profile_makers.by_name.insert(
InternedString::new("release"),
- ProfileMaker::new(Profile::default_release(), profiles.get("release").cloned()),
+ ProfileMaker::new(
+ Profile::default_release(trim_paths_enabled),
+ profiles.get("release").cloned(),
+ ),
);
}
@@ -142,21 +151,21 @@ impl Profiles {
(
"bench",
TomlProfile {
- inherits: Some(InternedString::new("release")),
+ inherits: Some(String::from("release")),
..TomlProfile::default()
},
),
(
"test",
TomlProfile {
- inherits: Some(InternedString::new("dev")),
+ inherits: Some(String::from("dev")),
..TomlProfile::default()
},
),
(
"doc",
TomlProfile {
- inherits: Some(InternedString::new("dev")),
+ inherits: Some(String::from("dev")),
..TomlProfile::default()
},
),
@@ -173,7 +182,7 @@ impl Profiles {
match &profile.dir_name {
None => {}
Some(dir_name) => {
- self.dir_names.insert(name, dir_name.to_owned());
+ self.dir_names.insert(name, InternedString::new(dir_name));
}
}
@@ -212,12 +221,13 @@ impl Profiles {
set: &mut HashSet<InternedString>,
profiles: &BTreeMap<InternedString, TomlProfile>,
) -> CargoResult<ProfileMaker> {
- let mut maker = match profile.inherits {
+ let mut maker = match &profile.inherits {
Some(inherits_name) if inherits_name == "dev" || inherits_name == "release" => {
// These are the root profiles added in `add_root_profiles`.
- self.get_profile_maker(inherits_name).unwrap().clone()
+ self.get_profile_maker(&inherits_name).unwrap().clone()
}
Some(inherits_name) => {
+ let inherits_name = InternedString::new(&inherits_name);
if !set.insert(inherits_name) {
bail!(
"profile inheritance loop detected with profile `{}` inheriting `{}`",
@@ -263,7 +273,7 @@ impl Profiles {
unit_for: UnitFor,
kind: CompileKind,
) -> Profile {
- let maker = self.get_profile_maker(self.requested_profile).unwrap();
+ let maker = self.get_profile_maker(&self.requested_profile).unwrap();
let mut profile = maker.get_profile(Some(pkg_id), is_member, unit_for.is_for_host());
// Dealing with `panic=abort` and `panic=unwind` requires some special
@@ -317,6 +327,7 @@ impl Profiles {
result.root = for_unit_profile.root;
result.debuginfo = for_unit_profile.debuginfo;
result.opt_level = for_unit_profile.opt_level;
+ result.trim_paths = for_unit_profile.trim_paths.clone();
result
}
@@ -325,7 +336,7 @@ impl Profiles {
/// select for the package that was actually built.
pub fn base_profile(&self) -> Profile {
let profile_name = self.requested_profile;
- let maker = self.get_profile_maker(profile_name).unwrap();
+ let maker = self.get_profile_maker(&profile_name).unwrap();
maker.get_profile(None, /*is_member*/ true, /*is_for_host*/ false)
}
@@ -372,9 +383,9 @@ impl Profiles {
}
/// Returns the profile maker for the given profile name.
- fn get_profile_maker(&self, name: InternedString) -> CargoResult<&ProfileMaker> {
+ fn get_profile_maker(&self, name: &str) -> CargoResult<&ProfileMaker> {
self.by_name
- .get(&name)
+ .get(name)
.ok_or_else(|| anyhow::format_err!("profile `{}` is not defined", name))
}
}
@@ -521,7 +532,7 @@ fn merge_profile(profile: &mut Profile, toml: &TomlProfile) {
None => {}
}
if toml.codegen_backend.is_some() {
- profile.codegen_backend = toml.codegen_backend;
+ profile.codegen_backend = toml.codegen_backend.as_ref().map(InternedString::from);
}
if toml.codegen_units.is_some() {
profile.codegen_units = toml.codegen_units;
@@ -553,7 +564,10 @@ fn merge_profile(profile: &mut Profile, toml: &TomlProfile) {
profile.incremental = incremental;
}
if let Some(flags) = &toml.rustflags {
- profile.rustflags = flags.clone();
+ profile.rustflags = flags.iter().map(InternedString::from).collect();
+ }
+ if let Some(trim_paths) = &toml.trim_paths {
+ profile.trim_paths = Some(trim_paths.clone());
}
profile.strip = match toml.strip {
Some(StringOrBool::Bool(true)) => Strip::Named(InternedString::new("symbols")),
@@ -598,6 +612,9 @@ pub struct Profile {
#[serde(skip_serializing_if = "Vec::is_empty")] // remove when `rustflags` is stablized
// Note that `rustflags` is used for the cargo-feature `profile_rustflags`
pub rustflags: Vec<InternedString>,
+ // remove when `-Ztrim-paths` is stablized
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub trim_paths: Option<TomlTrimPaths>,
}
impl Default for Profile {
@@ -618,6 +635,7 @@ impl Default for Profile {
panic: PanicStrategy::Unwind,
strip: Strip::None,
rustflags: vec![],
+ trim_paths: None,
}
}
}
@@ -627,7 +645,7 @@ compact_debug! {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let (default, default_name) = match self.name.as_str() {
"dev" => (Profile::default_dev(), "default_dev()"),
- "release" => (Profile::default_release(), "default_release()"),
+ "release" => (Profile::default_release(false), "default_release()"),
_ => (Profile::default(), "default()"),
};
[debug_the_fields(
@@ -646,6 +664,7 @@ compact_debug! {
panic
strip
rustflags
+ trim_paths
)]
}
}
@@ -687,11 +706,13 @@ impl Profile {
}
/// Returns a built-in `release` profile.
- fn default_release() -> Profile {
+ fn default_release(trim_paths_enabled: bool) -> Profile {
+ let trim_paths = trim_paths_enabled.then(|| TomlTrimPathsValue::Object.into());
Profile {
name: InternedString::new("release"),
root: ProfileRoot::Release,
opt_level: InternedString::new("3"),
+ trim_paths,
..Profile::default()
}
}
@@ -712,6 +733,7 @@ impl Profile {
self.rpath,
(self.incremental, self.panic, self.strip),
&self.rustflags,
+ &self.trim_paths,
)
}
}
@@ -1162,7 +1184,11 @@ fn merge_config_profiles(
requested_profile: InternedString,
) -> CargoResult<BTreeMap<InternedString, TomlProfile>> {
let mut profiles = match ws.profiles() {
- Some(profiles) => profiles.get_all().clone(),
+ Some(profiles) => profiles
+ .get_all()
+ .iter()
+ .map(|(k, v)| (InternedString::new(k), v.clone()))
+ .collect(),
None => BTreeMap::new(),
};
// Set of profile names to check if defined in config only.
@@ -1174,7 +1200,7 @@ fn merge_config_profiles(
profile.merge(&config_profile);
}
if let Some(inherits) = &profile.inherits {
- check_to_add.insert(*inherits);
+ check_to_add.insert(InternedString::new(inherits));
}
}
// Add the built-in profiles. This is important for things like `cargo
@@ -1188,10 +1214,10 @@ fn merge_config_profiles(
while !check_to_add.is_empty() {
std::mem::swap(&mut current, &mut check_to_add);
for name in current.drain() {
- if !profiles.contains_key(&name) {
+ if !profiles.contains_key(name.as_str()) {
if let Some(config_profile) = get_config_profile(ws, &name)? {
if let Some(inherits) = &config_profile.inherits {
- check_to_add.insert(*inherits);
+ check_to_add.insert(InternedString::new(inherits));
}
profiles.insert(name, config_profile);
}
diff --git a/src/tools/cargo/src/cargo/core/registry.rs b/src/tools/cargo/src/cargo/core/registry.rs
index 9a6a5a035..a91f2986a 100644
--- a/src/tools/cargo/src/cargo/core/registry.rs
+++ b/src/tools/cargo/src/cargo/core/registry.rs
@@ -116,7 +116,7 @@ enum Kind {
/// directive that we found in a lockfile, if present.
pub struct LockedPatchDependency {
/// The original `Dependency` directive, except "locked" so it's version
- /// requirement is `=foo` and its `SourceId` has a "precise" listed.
+ /// requirement is Locked to `foo` and its `SourceId` has a "precise" listed.
pub dependency: Dependency,
/// The `PackageId` that was previously found in a lock file which
/// `dependency` matches.
@@ -161,7 +161,7 @@ impl<'cfg> PackageRegistry<'cfg> {
// If the previous source was not a precise source, then we can be
// sure that it's already been updated if we've already loaded it.
- Some((previous, _)) if previous.precise().is_none() => {
+ Some((previous, _)) if !previous.has_precise() => {
debug!("load/precise {}", namespace);
return Ok(());
}
@@ -170,7 +170,7 @@ impl<'cfg> PackageRegistry<'cfg> {
// then we're done, otherwise we need to need to move forward
// updating this source.
Some((previous, _)) => {
- if previous.precise() == namespace.precise() {
+ if previous.has_same_precise_as(namespace) {
debug!("load/match {}", namespace);
return Ok(());
}
@@ -471,9 +471,9 @@ impl<'cfg> PackageRegistry<'cfg> {
//
// If we have a precise version, then we'll update lazily during the
// querying phase. Note that precise in this case is only
- // `Some("locked")` as other `Some` values indicate a `cargo update
+ // `"locked"` as other values indicate a `cargo update
// --precise` request
- if source_id.precise() != Some("locked") {
+ if !source_id.has_locked_precise() {
self.sources.get_mut(source_id).unwrap().invalidate_cache();
} else {
debug!("skipping update due to locked registry");
diff --git a/src/tools/cargo/src/cargo/core/resolver/context.rs b/src/tools/cargo/src/cargo/core/resolver/context.rs
index f19c678a6..09b16b39c 100644
--- a/src/tools/cargo/src/cargo/core/resolver/context.rs
+++ b/src/tools/cargo/src/cargo/core/resolver/context.rs
@@ -10,10 +10,6 @@ use std::collections::HashMap;
use std::num::NonZeroU64;
use tracing::debug;
-pub use super::encode::Metadata;
-pub use super::encode::{EncodableDependency, EncodablePackageId, EncodableResolve};
-pub use super::resolve::Resolve;
-
// A `Context` is basically a bunch of local resolution information which is
// kept around for all `BacktrackFrame` instances. As a result, this runs the
// risk of being cloned *a lot* so we want to make this as cheap to clone as
diff --git a/src/tools/cargo/src/cargo/core/resolver/dep_cache.rs b/src/tools/cargo/src/cargo/core/resolver/dep_cache.rs
index 9041c5b0f..6c904c148 100644
--- a/src/tools/cargo/src/cargo/core/resolver/dep_cache.rs
+++ b/src/tools/cargo/src/cargo/core/resolver/dep_cache.rs
@@ -20,7 +20,6 @@ use crate::core::{Dependency, FeatureValue, PackageId, PackageIdSpec, Registry,
use crate::sources::source::QueryKind;
use crate::util::errors::CargoResult;
use crate::util::interning::InternedString;
-use crate::util::RustVersion;
use anyhow::Context as _;
use std::collections::{BTreeSet, HashMap, HashSet};
@@ -32,16 +31,11 @@ pub struct RegistryQueryer<'a> {
pub registry: &'a mut (dyn Registry + 'a),
replacements: &'a [(PackageIdSpec, Dependency)],
version_prefs: &'a VersionPreferences,
- /// If set the list of dependency candidates will be sorted by minimal
- /// versions first. That allows `cargo update -Z minimal-versions` which will
- /// specify minimum dependency versions to be used.
- minimal_versions: bool,
- max_rust_version: Option<RustVersion>,
- /// a cache of `Candidate`s that fulfil a `Dependency` (and whether `first_minimal_version`)
- registry_cache: HashMap<(Dependency, bool), Poll<Rc<Vec<Summary>>>>,
+ /// a cache of `Candidate`s that fulfil a `Dependency` (and whether `first_version`)
+ registry_cache: HashMap<(Dependency, Option<VersionOrdering>), Poll<Rc<Vec<Summary>>>>,
/// a cache of `Dependency`s that are required for a `Summary`
///
- /// HACK: `first_minimal_version` is not kept in the cache key is it is 1:1 with
+ /// HACK: `first_version` is not kept in the cache key is it is 1:1 with
/// `parent.is_none()` (the first element of the cache key) as it doesn't change through
/// execution.
summary_cache: HashMap<
@@ -57,15 +51,11 @@ impl<'a> RegistryQueryer<'a> {
registry: &'a mut dyn Registry,
replacements: &'a [(PackageIdSpec, Dependency)],
version_prefs: &'a VersionPreferences,
- minimal_versions: bool,
- max_rust_version: Option<&RustVersion>,
) -> Self {
RegistryQueryer {
registry,
replacements,
version_prefs,
- minimal_versions,
- max_rust_version: max_rust_version.cloned(),
registry_cache: HashMap::new(),
summary_cache: HashMap::new(),
used_replacements: HashMap::new(),
@@ -106,23 +96,20 @@ impl<'a> RegistryQueryer<'a> {
pub fn query(
&mut self,
dep: &Dependency,
- first_minimal_version: bool,
+ first_version: Option<VersionOrdering>,
) -> Poll<CargoResult<Rc<Vec<Summary>>>> {
- let registry_cache_key = (dep.clone(), first_minimal_version);
+ let registry_cache_key = (dep.clone(), first_version);
if let Some(out) = self.registry_cache.get(&registry_cache_key).cloned() {
return out.map(Result::Ok);
}
let mut ret = Vec::new();
let ready = self.registry.query(dep, QueryKind::Exact, &mut |s| {
- if self.max_rust_version.is_none() || s.rust_version() <= self.max_rust_version.as_ref()
- {
- ret.push(s);
- }
+ ret.push(s);
})?;
if ready.is_pending() {
self.registry_cache
- .insert((dep.clone(), first_minimal_version), Poll::Pending);
+ .insert((dep.clone(), first_version), Poll::Pending);
return Poll::Pending;
}
for summary in ret.iter() {
@@ -144,7 +131,7 @@ impl<'a> RegistryQueryer<'a> {
Poll::Ready(s) => s.into_iter(),
Poll::Pending => {
self.registry_cache
- .insert((dep.clone(), first_minimal_version), Poll::Pending);
+ .insert((dep.clone(), first_version), Poll::Pending);
return Poll::Pending;
}
};
@@ -215,16 +202,8 @@ impl<'a> RegistryQueryer<'a> {
}
}
- // When we attempt versions for a package we'll want to do so in a sorted fashion to pick
- // the "best candidates" first. VersionPreferences implements this notion.
- let ordering = if first_minimal_version || self.minimal_versions {
- VersionOrdering::MinimumVersionsFirst
- } else {
- VersionOrdering::MaximumVersionsFirst
- };
- let first_version = first_minimal_version;
- self.version_prefs
- .sort_summaries(&mut ret, ordering, first_version);
+ let first_version = first_version;
+ self.version_prefs.sort_summaries(&mut ret, first_version);
let out = Poll::Ready(Rc::new(ret));
@@ -243,7 +222,7 @@ impl<'a> RegistryQueryer<'a> {
parent: Option<PackageId>,
candidate: &Summary,
opts: &ResolveOpts,
- first_minimal_version: bool,
+ first_version: Option<VersionOrdering>,
) -> ActivateResult<Rc<(HashSet<InternedString>, Rc<Vec<DepInfo>>)>> {
// if we have calculated a result before, then we can just return it,
// as it is a "pure" query of its arguments.
@@ -263,24 +242,22 @@ impl<'a> RegistryQueryer<'a> {
let mut all_ready = true;
let mut deps = deps
.into_iter()
- .filter_map(
- |(dep, features)| match self.query(&dep, first_minimal_version) {
- Poll::Ready(Ok(candidates)) => Some(Ok((dep, candidates, features))),
- Poll::Pending => {
- all_ready = false;
- // we can ignore Pending deps, resolve will be repeatedly called
- // until there are none to ignore
- None
- }
- Poll::Ready(Err(e)) => Some(Err(e).with_context(|| {
- format!(
- "failed to get `{}` as a dependency of {}",
- dep.package_name(),
- describe_path_in_context(cx, &candidate.package_id()),
- )
- })),
- },
- )
+ .filter_map(|(dep, features)| match self.query(&dep, first_version) {
+ Poll::Ready(Ok(candidates)) => Some(Ok((dep, candidates, features))),
+ Poll::Pending => {
+ all_ready = false;
+ // we can ignore Pending deps, resolve will be repeatedly called
+ // until there are none to ignore
+ None
+ }
+ Poll::Ready(Err(e)) => Some(Err(e).with_context(|| {
+ format!(
+ "failed to get `{}` as a dependency of {}",
+ dep.package_name(),
+ describe_path_in_context(cx, &candidate.package_id()),
+ )
+ })),
+ })
.collect::<CargoResult<Vec<DepInfo>>>()?;
// Attempt to resolve dependencies with fewer candidates before trying
diff --git a/src/tools/cargo/src/cargo/core/resolver/encode.rs b/src/tools/cargo/src/cargo/core/resolver/encode.rs
index 7835c2219..fcef1578a 100644
--- a/src/tools/cargo/src/cargo/core/resolver/encode.rs
+++ b/src/tools/cargo/src/cargo/core/resolver/encode.rs
@@ -776,7 +776,7 @@ pub fn encodable_package_id(
}
}
}
- let mut source = encodable_source_id(id_to_encode.with_precise(None), resolve_version);
+ let mut source = encodable_source_id(id_to_encode.without_precise(), resolve_version);
if let Some(counts) = &state.counts {
let version_counts = &counts[&id.name()];
if version_counts[&id.version()] == 1 {
diff --git a/src/tools/cargo/src/cargo/core/resolver/errors.rs b/src/tools/cargo/src/cargo/core/resolver/errors.rs
index b57a7c3eb..15a006ffb 100644
--- a/src/tools/cargo/src/cargo/core/resolver/errors.rs
+++ b/src/tools/cargo/src/cargo/core/resolver/errors.rs
@@ -4,7 +4,8 @@ use std::task::Poll;
use crate::core::{Dependency, PackageId, Registry, Summary};
use crate::sources::source::QueryKind;
use crate::util::edit_distance::edit_distance;
-use crate::util::{Config, OptVersionReq, VersionExt};
+use crate::util::{Config, OptVersionReq};
+use crate::util_semver::VersionExt;
use anyhow::Error;
use super::context::Context;
diff --git a/src/tools/cargo/src/cargo/core/resolver/mod.rs b/src/tools/cargo/src/cargo/core/resolver/mod.rs
index 7d8e8acd4..ecb6f36e6 100644
--- a/src/tools/cargo/src/cargo/core/resolver/mod.rs
+++ b/src/tools/cargo/src/cargo/core/resolver/mod.rs
@@ -71,7 +71,6 @@ use crate::util::config::Config;
use crate::util::errors::CargoResult;
use crate::util::network::PollExt;
use crate::util::profile;
-use crate::util::RustVersion;
use self::context::Context;
use self::dep_cache::RegistryQueryer;
@@ -139,39 +138,18 @@ pub fn resolve(
version_prefs: &VersionPreferences,
config: Option<&Config>,
check_public_visible_dependencies: bool,
- mut max_rust_version: Option<&RustVersion>,
) -> CargoResult<Resolve> {
let _p = profile::start("resolving");
- let minimal_versions = match config {
- Some(config) => config.cli_unstable().minimal_versions,
- None => false,
- };
- let direct_minimal_versions = match config {
- Some(config) => config.cli_unstable().direct_minimal_versions,
- None => false,
+ let first_version = match config {
+ Some(config) if config.cli_unstable().direct_minimal_versions => {
+ Some(VersionOrdering::MinimumVersionsFirst)
+ }
+ _ => None,
};
- if !config
- .map(|c| c.cli_unstable().msrv_policy)
- .unwrap_or(false)
- {
- max_rust_version = None;
- }
- let mut registry = RegistryQueryer::new(
- registry,
- replacements,
- version_prefs,
- minimal_versions,
- max_rust_version,
- );
+ let mut registry = RegistryQueryer::new(registry, replacements, version_prefs);
let cx = loop {
let cx = Context::new(check_public_visible_dependencies);
- let cx = activate_deps_loop(
- cx,
- &mut registry,
- summaries,
- direct_minimal_versions,
- config,
- )?;
+ let cx = activate_deps_loop(cx, &mut registry, summaries, first_version, config)?;
if registry.reset_pending() {
break cx;
} else {
@@ -223,7 +201,7 @@ fn activate_deps_loop(
mut cx: Context,
registry: &mut RegistryQueryer<'_>,
summaries: &[(Summary, ResolveOpts)],
- direct_minimal_versions: bool,
+ first_version: Option<VersionOrdering>,
config: Option<&Config>,
) -> CargoResult<Context> {
let mut backtrack_stack = Vec::new();
@@ -241,7 +219,7 @@ fn activate_deps_loop(
registry,
None,
summary.clone(),
- direct_minimal_versions,
+ first_version,
opts,
);
match res {
@@ -441,13 +419,13 @@ fn activate_deps_loop(
dep.package_name(),
candidate.version()
);
- let direct_minimal_version = false; // this is an indirect dependency
+ let first_version = None; // this is an indirect dependency
let res = activate(
&mut cx,
registry,
Some((&parent, &dep)),
candidate,
- direct_minimal_version,
+ first_version,
&opts,
);
@@ -659,7 +637,7 @@ fn activate(
registry: &mut RegistryQueryer<'_>,
parent: Option<(&Summary, &Dependency)>,
candidate: Summary,
- first_minimal_version: bool,
+ first_version: Option<VersionOrdering>,
opts: &ResolveOpts,
) -> ActivateResult<Option<(DepsFrame, Duration)>> {
let candidate_pid = candidate.package_id();
@@ -716,7 +694,7 @@ fn activate(
parent.map(|p| p.0.package_id()),
&candidate,
opts,
- first_minimal_version,
+ first_version,
)?;
// Record what list of features is active for this package.
@@ -905,14 +883,14 @@ fn generalize_conflicting(
})
{
for critical_parents_dep in critical_parents_deps.iter() {
- // We only want `first_minimal_version=true` for direct dependencies of workspace
+ // We only want `first_version.is_some()` for direct dependencies of workspace
// members which isn't the case here as this has a `parent`
- let first_minimal_version = false;
+ let first_version = None;
// A dep is equivalent to one of the things it can resolve to.
// Thus, if all the things it can resolve to have already ben determined
// to be conflicting, then we can just say that we conflict with the parent.
if let Some(others) = registry
- .query(critical_parents_dep, first_minimal_version)
+ .query(critical_parents_dep, first_version)
.expect("an already used dep now error!?")
.expect("an already used dep now pending!?")
.iter()
diff --git a/src/tools/cargo/src/cargo/core/resolver/resolve.rs b/src/tools/cargo/src/cargo/core/resolver/resolve.rs
index 18a389773..b401e9232 100644
--- a/src/tools/cargo/src/cargo/core/resolver/resolve.rs
+++ b/src/tools/cargo/src/cargo/core/resolver/resolve.rs
@@ -88,6 +88,17 @@ pub enum ResolveVersion {
V4,
}
+impl ResolveVersion {
+ /// The maximum version of lockfile made into the stable channel.
+ ///
+ /// Any version larger than this needs `-Znext-lockfile-bump` to enable.
+ ///
+ /// Update this when you're going to stabilize a new lockfile format.
+ pub fn max_stable() -> ResolveVersion {
+ ResolveVersion::V3
+ }
+}
+
impl Resolve {
pub fn new(
graph: Graph<PackageId, HashSet<Dependency>>,
diff --git a/src/tools/cargo/src/cargo/core/resolver/version_prefs.rs b/src/tools/cargo/src/cargo/core/resolver/version_prefs.rs
index 28de77f11..0deef5565 100644
--- a/src/tools/cargo/src/cargo/core/resolver/version_prefs.rs
+++ b/src/tools/cargo/src/cargo/core/resolver/version_prefs.rs
@@ -6,6 +6,7 @@ use std::collections::{HashMap, HashSet};
use crate::core::{Dependency, PackageId, Summary};
use crate::util::interning::InternedString;
+use crate::util::RustVersion;
/// A collection of preferences for particular package versions.
///
@@ -18,9 +19,13 @@ use crate::util::interning::InternedString;
pub struct VersionPreferences {
try_to_use: HashSet<PackageId>,
prefer_patch_deps: HashMap<InternedString, HashSet<Dependency>>,
+ version_ordering: VersionOrdering,
+ max_rust_version: Option<RustVersion>,
}
+#[derive(Copy, Clone, Default, PartialEq, Eq, Hash, Debug)]
pub enum VersionOrdering {
+ #[default]
MaximumVersionsFirst,
MinimumVersionsFirst,
}
@@ -39,14 +44,29 @@ impl VersionPreferences {
.insert(dep);
}
- /// Sort the given vector of summaries in-place, with all summaries presumed to be for
- /// the same package. Preferred versions appear first in the result, sorted by
- /// `version_ordering`, followed by non-preferred versions sorted the same way.
+ pub fn version_ordering(&mut self, ordering: VersionOrdering) {
+ self.version_ordering = ordering;
+ }
+
+ pub fn max_rust_version(&mut self, ver: Option<RustVersion>) {
+ self.max_rust_version = ver;
+ }
+
+ /// Sort (and filter) the given vector of summaries in-place
+ ///
+ /// Note: all summaries presumed to be for the same package.
+ ///
+ /// Sort order:
+ /// 1. Preferred packages
+ /// 2. `first_version`, falling back to [`VersionPreferences::version_ordering`] when `None`
+ ///
+ /// Filtering:
+ /// - [`VersionPreferences::max_rust_version`]
+ /// - `first_version`
pub fn sort_summaries(
&self,
summaries: &mut Vec<Summary>,
- version_ordering: VersionOrdering,
- first_version: bool,
+ first_version: Option<VersionOrdering>,
) {
let should_prefer = |pkg_id: &PackageId| {
self.try_to_use.contains(pkg_id)
@@ -56,22 +76,24 @@ impl VersionPreferences {
.map(|deps| deps.iter().any(|d| d.matches_id(*pkg_id)))
.unwrap_or(false)
};
+ if self.max_rust_version.is_some() {
+ summaries.retain(|s| s.rust_version() <= self.max_rust_version.as_ref());
+ }
summaries.sort_unstable_by(|a, b| {
let prefer_a = should_prefer(&a.package_id());
let prefer_b = should_prefer(&b.package_id());
let previous_cmp = prefer_a.cmp(&prefer_b).reverse();
- match previous_cmp {
- Ordering::Equal => {
- let cmp = a.version().cmp(b.version());
- match version_ordering {
- VersionOrdering::MaximumVersionsFirst => cmp.reverse(),
- VersionOrdering::MinimumVersionsFirst => cmp,
- }
- }
- _ => previous_cmp,
+ if previous_cmp != Ordering::Equal {
+ return previous_cmp;
+ }
+
+ let cmp = a.version().cmp(b.version());
+ match first_version.unwrap_or(self.version_ordering) {
+ VersionOrdering::MaximumVersionsFirst => cmp.reverse(),
+ VersionOrdering::MinimumVersionsFirst => cmp,
}
});
- if first_version {
+ if first_version.is_some() {
let _ = summaries.split_off(1);
}
}
@@ -81,7 +103,6 @@ impl VersionPreferences {
mod test {
use super::*;
use crate::core::SourceId;
- use crate::util::RustVersion;
use std::collections::BTreeMap;
fn pkgid(name: &str, version: &str) -> PackageId {
@@ -96,7 +117,7 @@ mod test {
Dependency::parse(name, Some(version), src_id).unwrap()
}
- fn summ(name: &str, version: &str) -> Summary {
+ fn summ(name: &str, version: &str, msrv: Option<&str>) -> Summary {
let pkg_id = pkgid(name, version);
let features = BTreeMap::new();
Summary::new(
@@ -104,7 +125,7 @@ mod test {
Vec::new(),
&features,
None::<&String>,
- None::<RustVersion>,
+ msrv.map(|m| m.parse().unwrap()),
)
.unwrap()
}
@@ -123,19 +144,21 @@ mod test {
vp.prefer_package_id(pkgid("foo", "1.2.3"));
let mut summaries = vec![
- summ("foo", "1.2.4"),
- summ("foo", "1.2.3"),
- summ("foo", "1.1.0"),
- summ("foo", "1.0.9"),
+ summ("foo", "1.2.4", None),
+ summ("foo", "1.2.3", None),
+ summ("foo", "1.1.0", None),
+ summ("foo", "1.0.9", None),
];
- vp.sort_summaries(&mut summaries, VersionOrdering::MaximumVersionsFirst, false);
+ vp.version_ordering(VersionOrdering::MaximumVersionsFirst);
+ vp.sort_summaries(&mut summaries, None);
assert_eq!(
describe(&summaries),
"foo/1.2.3, foo/1.2.4, foo/1.1.0, foo/1.0.9".to_string()
);
- vp.sort_summaries(&mut summaries, VersionOrdering::MinimumVersionsFirst, false);
+ vp.version_ordering(VersionOrdering::MinimumVersionsFirst);
+ vp.sort_summaries(&mut summaries, None);
assert_eq!(
describe(&summaries),
"foo/1.2.3, foo/1.0.9, foo/1.1.0, foo/1.2.4".to_string()
@@ -148,19 +171,21 @@ mod test {
vp.prefer_dependency(dep("foo", "=1.2.3"));
let mut summaries = vec![
- summ("foo", "1.2.4"),
- summ("foo", "1.2.3"),
- summ("foo", "1.1.0"),
- summ("foo", "1.0.9"),
+ summ("foo", "1.2.4", None),
+ summ("foo", "1.2.3", None),
+ summ("foo", "1.1.0", None),
+ summ("foo", "1.0.9", None),
];
- vp.sort_summaries(&mut summaries, VersionOrdering::MaximumVersionsFirst, false);
+ vp.version_ordering(VersionOrdering::MaximumVersionsFirst);
+ vp.sort_summaries(&mut summaries, None);
assert_eq!(
describe(&summaries),
"foo/1.2.3, foo/1.2.4, foo/1.1.0, foo/1.0.9".to_string()
);
- vp.sort_summaries(&mut summaries, VersionOrdering::MinimumVersionsFirst, false);
+ vp.version_ordering(VersionOrdering::MinimumVersionsFirst);
+ vp.sort_summaries(&mut summaries, None);
assert_eq!(
describe(&summaries),
"foo/1.2.3, foo/1.0.9, foo/1.1.0, foo/1.2.4".to_string()
@@ -174,22 +199,51 @@ mod test {
vp.prefer_dependency(dep("foo", "=1.1.0"));
let mut summaries = vec![
- summ("foo", "1.2.4"),
- summ("foo", "1.2.3"),
- summ("foo", "1.1.0"),
- summ("foo", "1.0.9"),
+ summ("foo", "1.2.4", None),
+ summ("foo", "1.2.3", None),
+ summ("foo", "1.1.0", None),
+ summ("foo", "1.0.9", None),
];
- vp.sort_summaries(&mut summaries, VersionOrdering::MaximumVersionsFirst, false);
+ vp.version_ordering(VersionOrdering::MaximumVersionsFirst);
+ vp.sort_summaries(&mut summaries, None);
assert_eq!(
describe(&summaries),
"foo/1.2.3, foo/1.1.0, foo/1.2.4, foo/1.0.9".to_string()
);
- vp.sort_summaries(&mut summaries, VersionOrdering::MinimumVersionsFirst, false);
+ vp.version_ordering(VersionOrdering::MinimumVersionsFirst);
+ vp.sort_summaries(&mut summaries, None);
assert_eq!(
describe(&summaries),
"foo/1.1.0, foo/1.2.3, foo/1.0.9, foo/1.2.4".to_string()
);
}
+
+ #[test]
+ fn test_max_rust_version() {
+ let mut vp = VersionPreferences::default();
+ vp.max_rust_version(Some("1.50".parse().unwrap()));
+
+ let mut summaries = vec![
+ summ("foo", "1.2.4", Some("1.60")),
+ summ("foo", "1.2.3", Some("1.50")),
+ summ("foo", "1.1.0", Some("1.40")),
+ summ("foo", "1.0.9", None),
+ ];
+
+ vp.version_ordering(VersionOrdering::MaximumVersionsFirst);
+ vp.sort_summaries(&mut summaries, None);
+ assert_eq!(
+ describe(&summaries),
+ "foo/1.2.3, foo/1.1.0, foo/1.0.9".to_string()
+ );
+
+ vp.version_ordering(VersionOrdering::MinimumVersionsFirst);
+ vp.sort_summaries(&mut summaries, None);
+ assert_eq!(
+ describe(&summaries),
+ "foo/1.0.9, foo/1.1.0, foo/1.2.3".to_string()
+ );
+ }
}
diff --git a/src/tools/cargo/src/cargo/core/shell.rs b/src/tools/cargo/src/cargo/core/shell.rs
index a9b9e84af..3d446664f 100644
--- a/src/tools/cargo/src/cargo/core/shell.rs
+++ b/src/tools/cargo/src/cargo/core/shell.rs
@@ -6,6 +6,7 @@ use anstream::AutoStream;
use anstyle::Style;
use crate::util::errors::CargoResult;
+use crate::util::hostname;
use crate::util::style::*;
pub enum TtyWidth {
@@ -57,6 +58,7 @@ pub struct Shell {
/// Flag that indicates the current line needs to be cleared before
/// printing. Used when a progress bar is currently displayed.
needs_clear: bool,
+ hostname: Option<String>,
}
impl fmt::Debug for Shell {
@@ -85,6 +87,7 @@ enum ShellOut {
stderr: AutoStream<std::io::Stderr>,
stderr_tty: bool,
color_choice: ColorChoice,
+ hyperlinks: bool,
},
}
@@ -111,10 +114,12 @@ impl Shell {
stdout: AutoStream::new(std::io::stdout(), stdout_choice),
stderr: AutoStream::new(std::io::stderr(), stderr_choice),
color_choice: auto_clr,
+ hyperlinks: supports_hyperlinks(),
stderr_tty: std::io::stderr().is_terminal(),
},
verbosity: Verbosity::Verbose,
needs_clear: false,
+ hostname: None,
}
}
@@ -124,6 +129,7 @@ impl Shell {
output: ShellOut::Write(AutoStream::never(out)), // strip all formatting on write
verbosity: Verbosity::Verbose,
needs_clear: false,
+ hostname: None,
}
}
@@ -314,6 +320,16 @@ impl Shell {
Ok(())
}
+ pub fn set_hyperlinks(&mut self, yes: bool) -> CargoResult<()> {
+ if let ShellOut::Stream {
+ ref mut hyperlinks, ..
+ } = self.output
+ {
+ *hyperlinks = yes;
+ }
+ Ok(())
+ }
+
/// Gets the current color choice.
///
/// If we are not using a color stream, this will always return `Never`, even if the color
@@ -340,18 +356,59 @@ impl Shell {
}
}
- /// Write a styled fragment
- ///
- /// Caller is responsible for deciding whether [`Shell::verbosity`] is affects output.
- pub fn write_stdout(&mut self, fragment: impl fmt::Display, color: &Style) -> CargoResult<()> {
- self.output.write_stdout(fragment, color)
+ pub fn out_hyperlink<D: fmt::Display>(&self, url: D) -> Hyperlink<D> {
+ let supports_hyperlinks = match &self.output {
+ ShellOut::Write(_) => false,
+ ShellOut::Stream {
+ stdout, hyperlinks, ..
+ } => stdout.current_choice() == anstream::ColorChoice::AlwaysAnsi && *hyperlinks,
+ };
+ Hyperlink {
+ url: supports_hyperlinks.then_some(url),
+ }
}
- /// Write a styled fragment
- ///
- /// Caller is responsible for deciding whether [`Shell::verbosity`] is affects output.
- pub fn write_stderr(&mut self, fragment: impl fmt::Display, color: &Style) -> CargoResult<()> {
- self.output.write_stderr(fragment, color)
+ pub fn err_hyperlink<D: fmt::Display>(&self, url: D) -> Hyperlink<D> {
+ let supports_hyperlinks = match &self.output {
+ ShellOut::Write(_) => false,
+ ShellOut::Stream {
+ stderr, hyperlinks, ..
+ } => stderr.current_choice() == anstream::ColorChoice::AlwaysAnsi && *hyperlinks,
+ };
+ if supports_hyperlinks {
+ Hyperlink { url: Some(url) }
+ } else {
+ Hyperlink { url: None }
+ }
+ }
+
+ pub fn out_file_hyperlink(&mut self, path: &std::path::Path) -> Hyperlink<url::Url> {
+ let url = self.file_hyperlink(path);
+ url.map(|u| self.out_hyperlink(u)).unwrap_or_default()
+ }
+
+ pub fn err_file_hyperlink(&mut self, path: &std::path::Path) -> Hyperlink<url::Url> {
+ let url = self.file_hyperlink(path);
+ url.map(|u| self.err_hyperlink(u)).unwrap_or_default()
+ }
+
+ fn file_hyperlink(&mut self, path: &std::path::Path) -> Option<url::Url> {
+ let mut url = url::Url::from_file_path(path).ok()?;
+ // Do a best-effort of setting the host in the URL to avoid issues with opening a link
+ // scoped to the computer you've SSHed into
+ let hostname = if cfg!(windows) {
+ // Not supported correctly on windows
+ None
+ } else {
+ if let Some(hostname) = self.hostname.as_deref() {
+ Some(hostname)
+ } else {
+ self.hostname = hostname().ok().and_then(|h| h.into_string().ok());
+ self.hostname.as_deref()
+ }
+ };
+ let _ = url.set_host(hostname);
+ Some(url)
}
/// Prints a message to stderr and translates ANSI escape code into console colors.
@@ -416,28 +473,6 @@ impl ShellOut {
Ok(())
}
- /// Write a styled fragment
- fn write_stdout(&mut self, fragment: impl fmt::Display, style: &Style) -> CargoResult<()> {
- let style = style.render();
- let reset = anstyle::Reset.render();
-
- let mut buffer = Vec::new();
- write!(buffer, "{style}{}{reset}", fragment)?;
- self.stdout().write_all(&buffer)?;
- Ok(())
- }
-
- /// Write a styled fragment
- fn write_stderr(&mut self, fragment: impl fmt::Display, style: &Style) -> CargoResult<()> {
- let style = style.render();
- let reset = anstyle::Reset.render();
-
- let mut buffer = Vec::new();
- write!(buffer, "{style}{}{reset}", fragment)?;
- self.stderr().write_all(&buffer)?;
- Ok(())
- }
-
/// Gets stdout as a `io::Write`.
fn stdout(&mut self) -> &mut dyn Write {
match *self {
@@ -475,6 +510,44 @@ fn supports_color(choice: anstream::ColorChoice) -> bool {
}
}
+fn supports_hyperlinks() -> bool {
+ #[allow(clippy::disallowed_methods)] // We are reading the state of the system, not config
+ if std::env::var_os("TERM_PROGRAM").as_deref() == Some(std::ffi::OsStr::new("iTerm.app")) {
+ // Override `supports_hyperlinks` as we have an unknown incompatibility with iTerm2
+ return false;
+ }
+
+ supports_hyperlinks::supports_hyperlinks()
+}
+
+pub struct Hyperlink<D: fmt::Display> {
+ url: Option<D>,
+}
+
+impl<D: fmt::Display> Default for Hyperlink<D> {
+ fn default() -> Self {
+ Self { url: None }
+ }
+}
+
+impl<D: fmt::Display> Hyperlink<D> {
+ pub fn open(&self) -> impl fmt::Display {
+ if let Some(url) = self.url.as_ref() {
+ format!("\x1B]8;;{url}\x1B\\")
+ } else {
+ String::new()
+ }
+ }
+
+ pub fn close(&self) -> impl fmt::Display {
+ if self.url.is_some() {
+ "\x1B]8;;\x1B\\"
+ } else {
+ ""
+ }
+ }
+}
+
#[cfg(unix)]
mod imp {
use super::{Shell, TtyWidth};
diff --git a/src/tools/cargo/src/cargo/core/source_id.rs b/src/tools/cargo/src/cargo/core/source_id.rs
index d688b8739..e53b1704d 100644
--- a/src/tools/cargo/src/cargo/core/source_id.rs
+++ b/src/tools/cargo/src/cargo/core/source_id.rs
@@ -3,7 +3,8 @@ use crate::sources::registry::CRATES_IO_HTTP_INDEX;
use crate::sources::source::Source;
use crate::sources::{DirectorySource, CRATES_IO_DOMAIN, CRATES_IO_INDEX, CRATES_IO_REGISTRY};
use crate::sources::{GitSource, PathSource, RegistrySource};
-use crate::util::{config, CanonicalUrl, CargoResult, Config, IntoUrl, ToSemver};
+use crate::util::interning::InternedString;
+use crate::util::{config, CanonicalUrl, CargoResult, Config, IntoUrl};
use anyhow::Context;
use serde::de;
use serde::ser;
@@ -50,7 +51,7 @@ struct SourceIdInner {
/// The source kind.
kind: SourceKind,
/// For example, the exact Git revision of the specified branch for a Git Source.
- precise: Option<String>,
+ precise: Option<Precise>,
/// Name of the remote registry.
///
/// WARNING: this is not always set when the name is not known,
@@ -58,6 +59,29 @@ struct SourceIdInner {
registry_key: Option<KeyOf>,
}
+#[derive(Eq, PartialEq, Clone, Debug, Hash)]
+enum Precise {
+ Locked,
+ Updated {
+ name: InternedString,
+ from: semver::Version,
+ to: semver::Version,
+ },
+ GitUrlFragment(String),
+}
+
+impl fmt::Display for Precise {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ match self {
+ Precise::Locked => "locked".fmt(f),
+ Precise::Updated { name, from, to } => {
+ write!(f, "{name}={from}->{to}")
+ }
+ Precise::GitUrlFragment(s) => s.fmt(f),
+ }
+ }
+}
+
/// The possible kinds of code source.
/// Along with [`SourceIdInner`], this fully defines the source.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -164,31 +188,19 @@ impl SourceId {
match kind {
"git" => {
let mut url = url.into_url()?;
- let mut reference = GitReference::DefaultBranch;
- for (k, v) in url.query_pairs() {
- match &k[..] {
- // Map older 'ref' to branch.
- "branch" | "ref" => reference = GitReference::Branch(v.into_owned()),
-
- "rev" => reference = GitReference::Rev(v.into_owned()),
- "tag" => reference = GitReference::Tag(v.into_owned()),
- _ => {}
- }
- }
+ let reference = GitReference::from_query(url.query_pairs());
let precise = url.fragment().map(|s| s.to_owned());
url.set_fragment(None);
url.set_query(None);
- Ok(SourceId::for_git(&url, reference)?.with_precise(precise))
+ Ok(SourceId::for_git(&url, reference)?.with_git_precise(precise))
}
"registry" => {
let url = url.into_url()?;
- Ok(SourceId::new(SourceKind::Registry, url, None)?
- .with_precise(Some("locked".to_string())))
+ Ok(SourceId::new(SourceKind::Registry, url, None)?.with_locked_precise())
}
"sparse" => {
let url = string.into_url()?;
- Ok(SourceId::new(SourceKind::SparseRegistry, url, None)?
- .with_precise(Some("locked".to_string())))
+ Ok(SourceId::new(SourceKind::SparseRegistry, url, None)?.with_locked_precise())
}
"path" => {
let url = url.into_url()?;
@@ -332,10 +344,10 @@ impl SourceId {
pub fn display_registry_name(self) -> String {
if let Some(key) = self.inner.registry_key.as_ref().map(|k| k.key()) {
key.into()
- } else if self.precise().is_some() {
+ } else if self.has_precise() {
// We remove `precise` here to retrieve an permissive version of
// `SourceIdInner`, which may contain the registry name.
- self.with_precise(None).display_registry_name()
+ self.without_precise().display_registry_name()
} else {
url_display(self.url())
}
@@ -444,37 +456,81 @@ impl SourceId {
}
}
- /// Gets the value of the precise field.
- pub fn precise(self) -> Option<&'static str> {
- self.inner.precise.as_deref()
+ /// Check if the precise data field has bean set
+ pub fn has_precise(self) -> bool {
+ self.inner.precise.is_some()
+ }
+
+ /// Check if the precise data field has bean set to "locked"
+ pub fn has_locked_precise(self) -> bool {
+ self.inner.precise == Some(Precise::Locked)
+ }
+
+ /// Check if two sources have the same precise data field
+ pub fn has_same_precise_as(self, other: Self) -> bool {
+ self.inner.precise == other.inner.precise
}
/// Check if the precise data field stores information for this `name`
/// from a call to [SourceId::with_precise_registry_version].
///
/// If so return the version currently in the lock file and the version to be updated to.
- /// If specified, our own source will have a precise version listed of the form
- // `<pkg>=<p_req>-><f_req>` where `<pkg>` is the name of a crate on
- // this source, `<p_req>` is the version installed and `<f_req>` is the
- // version requested (argument to `--precise`).
pub fn precise_registry_version(
self,
- name: &str,
- ) -> Option<(semver::Version, semver::Version)> {
- self.inner
- .precise
- .as_deref()
- .and_then(|p| p.strip_prefix(name)?.strip_prefix('='))
- .map(|p| {
- let (current, requested) = p.split_once("->").unwrap();
- (current.to_semver().unwrap(), requested.to_semver().unwrap())
- })
+ pkg: &str,
+ ) -> Option<(&semver::Version, &semver::Version)> {
+ match &self.inner.precise {
+ Some(Precise::Updated { name, from, to }) if name == pkg => Some((from, to)),
+ _ => None,
+ }
+ }
+
+ pub fn precise_git_fragment(self) -> Option<&'static str> {
+ match &self.inner.precise {
+ Some(Precise::GitUrlFragment(s)) => Some(&s[..8]),
+ _ => None,
+ }
+ }
+
+ pub fn precise_git_oid(self) -> CargoResult<Option<git2::Oid>> {
+ Ok(match self.inner.precise.as_ref() {
+ Some(Precise::GitUrlFragment(s)) => {
+ Some(git2::Oid::from_str(s).with_context(|| {
+ format!("precise value for git is not a git revision: {}", s)
+ })?)
+ }
+ _ => None,
+ })
}
/// Creates a new `SourceId` from this source with the given `precise`.
- pub fn with_precise(self, v: Option<String>) -> SourceId {
+ pub fn with_git_precise(self, fragment: Option<String>) -> SourceId {
+ SourceId::wrap(SourceIdInner {
+ precise: fragment.map(|f| Precise::GitUrlFragment(f)),
+ ..(*self.inner).clone()
+ })
+ }
+
+ /// Creates a new `SourceId` from this source without a `precise`.
+ pub fn without_precise(self) -> SourceId {
SourceId::wrap(SourceIdInner {
- precise: v,
+ precise: None,
+ ..(*self.inner).clone()
+ })
+ }
+
+ /// Creates a new `SourceId` from this source without a `precise`.
+ pub fn with_locked_precise(self) -> SourceId {
+ SourceId::wrap(SourceIdInner {
+ precise: Some(Precise::Locked),
+ ..(*self.inner).clone()
+ })
+ }
+
+ /// Creates a new `SourceId` from this source with the `precise` from some other `SourceId`.
+ pub fn with_precise_from(self, v: Self) -> SourceId {
+ SourceId::wrap(SourceIdInner {
+ precise: v.inner.precise.clone(),
..(*self.inner).clone()
})
}
@@ -487,13 +543,21 @@ impl SourceId {
/// The data can be read with [SourceId::precise_registry_version]
pub fn with_precise_registry_version(
self,
- name: impl fmt::Display,
- version: &semver::Version,
+ name: InternedString,
+ version: semver::Version,
precise: &str,
) -> CargoResult<SourceId> {
- semver::Version::parse(precise)
+ let precise = semver::Version::parse(precise)
.with_context(|| format!("invalid version format for precise version `{precise}`"))?;
- Ok(self.with_precise(Some(format!("{}={}->{}", name, version, precise))))
+
+ Ok(SourceId::wrap(SourceIdInner {
+ precise: Some(Precise::Updated {
+ name,
+ from: version,
+ to: precise,
+ }),
+ ..(*self.inner).clone()
+ }))
}
/// Returns `true` if the remote registry is the standard <https://crates.io>.
@@ -625,7 +689,8 @@ impl fmt::Display for SourceId {
write!(f, "?{}", pretty)?;
}
- if let Some(ref s) = self.inner.precise {
+ if let Some(s) = &self.inner.precise {
+ let s = s.to_string();
let len = cmp::min(s.len(), 8);
write!(f, "#{}", &s[..len])?;
}
@@ -677,6 +742,20 @@ impl PartialEq for SourceIdInner {
}
}
+impl SourceKind {
+ pub(crate) fn protocol(&self) -> Option<&str> {
+ match self {
+ SourceKind::Path => Some("path"),
+ SourceKind::Git(_) => Some("git"),
+ SourceKind::Registry => Some("registry"),
+ // Sparse registry URL already includes the `sparse+` prefix
+ SourceKind::SparseRegistry => None,
+ SourceKind::LocalRegistry => Some("local-registry"),
+ SourceKind::Directory => Some("directory"),
+ }
+ }
+}
+
/// Forwards to `Ord`
impl PartialOrd for SourceKind {
fn partial_cmp(&self, other: &SourceKind) -> Option<Ordering> {
@@ -773,57 +852,46 @@ pub struct SourceIdAsUrl<'a> {
impl<'a> fmt::Display for SourceIdAsUrl<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- match *self.inner {
- SourceIdInner {
- kind: SourceKind::Path,
- ref url,
- ..
- } => write!(f, "path+{}", url),
- SourceIdInner {
- kind: SourceKind::Git(ref reference),
- ref url,
- ref precise,
- ..
- } => {
- write!(f, "git+{}", url)?;
- if let Some(pretty) = reference.pretty_ref(self.encoded) {
- write!(f, "?{}", pretty)?;
- }
- if let Some(precise) = precise.as_ref() {
- write!(f, "#{}", precise)?;
- }
- Ok(())
- }
- SourceIdInner {
- kind: SourceKind::Registry,
- ref url,
- ..
- } => {
- write!(f, "registry+{url}")
+ if let Some(protocol) = self.inner.kind.protocol() {
+ write!(f, "{protocol}+")?;
+ }
+ write!(f, "{}", self.inner.url)?;
+ if let SourceIdInner {
+ kind: SourceKind::Git(ref reference),
+ ref precise,
+ ..
+ } = *self.inner
+ {
+ if let Some(pretty) = reference.pretty_ref(self.encoded) {
+ write!(f, "?{}", pretty)?;
}
- SourceIdInner {
- kind: SourceKind::SparseRegistry,
- ref url,
- ..
- } => {
- // Sparse registry URL already includes the `sparse+` prefix
- write!(f, "{url}")
+ if let Some(precise) = precise.as_ref() {
+ write!(f, "#{}", precise)?;
}
- SourceIdInner {
- kind: SourceKind::LocalRegistry,
- ref url,
- ..
- } => write!(f, "local-registry+{}", url),
- SourceIdInner {
- kind: SourceKind::Directory,
- ref url,
- ..
- } => write!(f, "directory+{}", url),
}
+ Ok(())
}
}
impl GitReference {
+ pub fn from_query(
+ query_pairs: impl Iterator<Item = (impl AsRef<str>, impl AsRef<str>)>,
+ ) -> Self {
+ let mut reference = GitReference::DefaultBranch;
+ for (k, v) in query_pairs {
+ let v = v.as_ref();
+ match k.as_ref() {
+ // Map older 'ref' to branch.
+ "branch" | "ref" => reference = GitReference::Branch(v.to_owned()),
+
+ "rev" => reference = GitReference::Rev(v.to_owned()),
+ "tag" => reference = GitReference::Tag(v.to_owned()),
+ _ => {}
+ }
+ }
+ reference
+ }
+
/// Returns a `Display`able view of this git reference, or None if using
/// the head of the default branch
pub fn pretty_ref(&self, url_encoded: bool) -> Option<PrettyRef<'_>> {
diff --git a/src/tools/cargo/src/cargo/core/summary.rs b/src/tools/cargo/src/cargo/core/summary.rs
index 128c0db9c..243f6b398 100644
--- a/src/tools/cargo/src/cargo/core/summary.rs
+++ b/src/tools/cargo/src/cargo/core/summary.rs
@@ -431,6 +431,9 @@ impl fmt::Display for FeatureValue {
pub type FeatureMap = BTreeMap<InternedString, Vec<FeatureValue>>;
fn validate_feature_name(pkg_id: PackageId, name: &str) -> CargoResult<()> {
+ if name.is_empty() {
+ bail!("feature name cannot be empty");
+ }
let mut chars = name.chars();
if let Some(ch) = chars.next() {
if !(unicode_xid::UnicodeXID::is_xid_start(ch) || ch == '_' || ch.is_digit(10)) {
@@ -448,7 +451,7 @@ fn validate_feature_name(pkg_id: PackageId, name: &str) -> CargoResult<()> {
if !(unicode_xid::UnicodeXID::is_xid_continue(ch) || ch == '-' || ch == '+' || ch == '.') {
bail!(
"invalid character `{}` in feature `{}` in package {}, \
- characters must be Unicode XID characters, `+`, or `.` \
+ characters must be Unicode XID characters, '-', `+`, or `.` \
(numbers, `+`, `-`, `_`, `.`, or most letters)",
ch,
name,
@@ -488,5 +491,6 @@ mod tests {
assert!(validate_feature_name(pkg_id, "?foo").is_err());
assert!(validate_feature_name(pkg_id, "ⒶⒷⒸ").is_err());
assert!(validate_feature_name(pkg_id, "a¼").is_err());
+ assert!(validate_feature_name(pkg_id, "").is_err());
}
}
diff --git a/src/tools/cargo/src/cargo/core/workspace.rs b/src/tools/cargo/src/cargo/core/workspace.rs
index db379d780..4667c8029 100644
--- a/src/tools/cargo/src/cargo/core/workspace.rs
+++ b/src/tools/cargo/src/cargo/core/workspace.rs
@@ -22,7 +22,9 @@ use crate::sources::{PathSource, CRATES_IO_INDEX, CRATES_IO_REGISTRY};
use crate::util::edit_distance;
use crate::util::errors::{CargoResult, ManifestError};
use crate::util::interning::InternedString;
-use crate::util::toml::{read_manifest, InheritableFields, TomlDependency, TomlProfiles};
+use crate::util::toml::{
+ read_manifest, schema::InheritableFields, schema::TomlDependency, schema::TomlProfiles,
+};
use crate::util::RustVersion;
use crate::util::{config::ConfigRelativePath, Config, Filesystem, IntoUrl};
use cargo_util::paths;
@@ -1491,7 +1493,7 @@ impl<'cfg> Workspace<'cfg> {
// Check if `dep_name` is member of the workspace, but isn't associated with current package.
self.current_opt() != Some(member) && member.name() == *dep_name
});
- if is_member && specs.iter().any(|spec| spec.name() == *dep_name) {
+ if is_member && specs.iter().any(|spec| spec.name() == dep_name.as_str()) {
member_specific_features
.entry(*dep_name)
.or_default()
diff --git a/src/tools/cargo/src/cargo/lib.rs b/src/tools/cargo/src/cargo/lib.rs
index 908ff4ecc..6947642c9 100644
--- a/src/tools/cargo/src/cargo/lib.rs
+++ b/src/tools/cargo/src/cargo/lib.rs
@@ -93,7 +93,7 @@
//! Files that interact with cargo include
//!
//! - Package
-//! - `Cargo.toml`: User-written project manifest, loaded with [`util::toml::TomlManifest`] and then
+//! - `Cargo.toml`: User-written project manifest, loaded with [`util::toml::schema::TomlManifest`] and then
//! translated to [`core::manifest::Manifest`] which maybe stored in a [`core::Package`].
//! - This is editable with [`util::toml_mut::manifest::LocalManifest`]
//! - `Cargo.lock`: Generally loaded with [`ops::resolve_ws`] or a variant of it into a [`core::resolver::Resolve`]
@@ -161,6 +161,7 @@ pub mod core;
pub mod ops;
pub mod sources;
pub mod util;
+pub mod util_semver;
mod version;
pub fn exit_with_error(err: CliError, shell: &mut Shell) -> ! {
diff --git a/src/tools/cargo/src/cargo/ops/cargo_add/mod.rs b/src/tools/cargo/src/cargo/ops/cargo_add/mod.rs
index 968d6068f..39e37b156 100644
--- a/src/tools/cargo/src/cargo/ops/cargo_add/mod.rs
+++ b/src/tools/cargo/src/cargo/ops/cargo_add/mod.rs
@@ -23,6 +23,7 @@ use crate::core::Shell;
use crate::core::Summary;
use crate::core::Workspace;
use crate::sources::source::QueryKind;
+use crate::util::cache_lock::CacheLockMode;
use crate::util::style;
use crate::util::toml_mut::dependency::Dependency;
use crate::util::toml_mut::dependency::GitSource;
@@ -30,6 +31,7 @@ use crate::util::toml_mut::dependency::MaybeWorkspace;
use crate::util::toml_mut::dependency::PathSource;
use crate::util::toml_mut::dependency::Source;
use crate::util::toml_mut::dependency::WorkspaceSource;
+use crate::util::toml_mut::is_sorted;
use crate::util::toml_mut::manifest::DepTable;
use crate::util::toml_mut::manifest::LocalManifest;
use crate::util::RustVersion;
@@ -77,7 +79,9 @@ pub fn add(workspace: &Workspace<'_>, options: &AddOptions<'_>) -> CargoResult<(
let mut registry = PackageRegistry::new(options.config)?;
let deps = {
- let _lock = options.config.acquire_package_cache_lock()?;
+ let _lock = options
+ .config
+ .acquire_package_cache_lock(CacheLockMode::DownloadExclusive)?;
registry.lock_patches();
options
.dependencies
@@ -944,12 +948,17 @@ fn print_dep_table_msg(shell: &mut Shell, dep: &DependencyUI) -> CargoResult<()>
return Ok(());
}
+ let stderr = shell.err();
+ let good = style::GOOD.render();
+ let error = style::ERROR.render();
+ let reset = anstyle::Reset.render();
+
let (activated, deactivated) = dep.features();
if !activated.is_empty() || !deactivated.is_empty() {
let prefix = format!("{:>13}", " ");
let suffix = format_features_version_suffix(&dep);
- shell.write_stderr(format_args!("{prefix}Features{suffix}:\n"), &style::NOP)?;
+ writeln!(stderr, "{prefix}Features{suffix}:")?;
const MAX_FEATURE_PRINTS: usize = 30;
let total_activated = activated.len();
@@ -957,28 +966,18 @@ fn print_dep_table_msg(shell: &mut Shell, dep: &DependencyUI) -> CargoResult<()>
if total_activated <= MAX_FEATURE_PRINTS {
for feat in activated {
- shell.write_stderr(&prefix, &style::NOP)?;
- shell.write_stderr('+', &style::GOOD)?;
- shell.write_stderr(format_args!(" {feat}\n"), &style::NOP)?;
+ writeln!(stderr, "{prefix}{good}+{reset} {feat}")?;
}
} else {
- shell.write_stderr(
- format_args!("{prefix}{total_activated} activated features\n"),
- &style::NOP,
- )?;
+ writeln!(stderr, "{prefix}{total_activated} activated features")?;
}
if total_activated + total_deactivated <= MAX_FEATURE_PRINTS {
for feat in deactivated {
- shell.write_stderr(&prefix, &style::NOP)?;
- shell.write_stderr('-', &style::ERROR)?;
- shell.write_stderr(format_args!(" {feat}\n"), &style::NOP)?;
+ writeln!(stderr, "{prefix}{error}-{reset} {feat}")?;
}
} else {
- shell.write_stderr(
- format_args!("{prefix}{total_deactivated} deactivated features\n"),
- &style::NOP,
- )?;
+ writeln!(stderr, "{prefix}{total_deactivated} deactivated features")?;
}
}
@@ -1006,22 +1005,6 @@ fn format_features_version_suffix(dep: &DependencyUI) -> String {
}
}
-// Based on Iterator::is_sorted from nightly std; remove in favor of that when stabilized.
-fn is_sorted(mut it: impl Iterator<Item = impl PartialOrd>) -> bool {
- let Some(mut last) = it.next() else {
- return true;
- };
-
- for curr in it {
- if curr < last {
- return false;
- }
- last = curr;
- }
-
- true
-}
-
fn find_workspace_dep(toml_key: &str, root_manifest: &Path) -> CargoResult<Dependency> {
let manifest = LocalManifest::try_new(root_manifest)?;
let manifest = manifest
diff --git a/src/tools/cargo/src/cargo/ops/cargo_compile/mod.rs b/src/tools/cargo/src/cargo/ops/cargo_compile/mod.rs
index 9cf8599c4..94c6cf9de 100644
--- a/src/tools/cargo/src/cargo/ops/cargo_compile/mod.rs
+++ b/src/tools/cargo/src/cargo/ops/cargo_compile/mod.rs
@@ -493,7 +493,7 @@ pub fn create_bcx<'a, 'cfg>(
continue;
};
- let req = version.caret_req();
+ let req = version.to_caret_req();
if req.matches(&untagged_version) {
continue;
}
diff --git a/src/tools/cargo/src/cargo/ops/cargo_doc.rs b/src/tools/cargo/src/cargo/ops/cargo_doc.rs
index afa6ac327..ecc17e9fc 100644
--- a/src/tools/cargo/src/cargo/ops/cargo_doc.rs
+++ b/src/tools/cargo/src/cargo/ops/cargo_doc.rs
@@ -34,11 +34,31 @@ pub fn doc(ws: &Workspace<'_>, options: &DocOptions) -> CargoResult<()> {
let cfg: Option<PathAndArgs> = ws.config().get("doc.browser")?;
cfg.map(|path_args| (path_args.path.resolve_program(ws.config()), path_args.args))
};
-
let mut shell = ws.config().shell();
- shell.status("Opening", path.display())?;
+ let link = shell.err_file_hyperlink(&path);
+ shell.status(
+ "Opening",
+ format!("{}{}{}", link.open(), path.display(), link.close()),
+ )?;
open_docs(&path, &mut shell, config_browser, ws.config())?;
}
+ } else {
+ for name in &compilation.root_crate_names {
+ for kind in &options.compile_opts.build_config.requested_kinds {
+ let path = compilation.root_output[&kind]
+ .with_file_name("doc")
+ .join(&name)
+ .join("index.html");
+ if path.exists() {
+ let mut shell = ws.config().shell();
+ let link = shell.err_file_hyperlink(&path);
+ shell.status(
+ "Generated",
+ format!("{}{}{}", link.open(), path.display(), link.close()),
+ )?;
+ }
+ }
+ }
}
Ok(())
diff --git a/src/tools/cargo/src/cargo/ops/cargo_generate_lockfile.rs b/src/tools/cargo/src/cargo/ops/cargo_generate_lockfile.rs
index a83a92ccc..a16d6d403 100644
--- a/src/tools/cargo/src/cargo/ops/cargo_generate_lockfile.rs
+++ b/src/tools/cargo/src/cargo/ops/cargo_generate_lockfile.rs
@@ -3,10 +3,12 @@ use crate::core::resolver::features::{CliFeatures, HasDevUnits};
use crate::core::{PackageId, PackageIdSpec};
use crate::core::{Resolve, SourceId, Workspace};
use crate::ops;
+use crate::util::cache_lock::CacheLockMode;
use crate::util::config::Config;
use crate::util::style;
use crate::util::CargoResult;
use anstyle::Style;
+use std::cmp::Ordering;
use std::collections::{BTreeMap, HashSet};
use tracing::debug;
@@ -48,7 +50,9 @@ pub fn update_lockfile(ws: &Workspace<'_>, opts: &UpdateOptions<'_>) -> CargoRes
// Updates often require a lot of modifications to the registry, so ensure
// that we're synchronized against other Cargos.
- let _lock = ws.config().acquire_package_cache_lock()?;
+ let _lock = ws
+ .config()
+ .acquire_package_cache_lock(CacheLockMode::DownloadExclusive)?;
let max_rust_version = ws.rust_version();
@@ -101,14 +105,14 @@ pub fn update_lockfile(ws: &Workspace<'_>, opts: &UpdateOptions<'_>) -> CargoRes
if pid.source_id().is_registry() {
pid.source_id().with_precise_registry_version(
pid.name(),
- pid.version(),
+ pid.version().clone(),
precise,
)?
} else {
- pid.source_id().with_precise(Some(precise.to_string()))
+ pid.source_id().with_git_precise(Some(precise.to_string()))
}
}
- None => pid.source_id().with_precise(None),
+ None => pid.source_id().without_precise(),
});
}
if let Ok(unused_id) =
@@ -143,13 +147,17 @@ pub fn update_lockfile(ws: &Workspace<'_>, opts: &UpdateOptions<'_>) -> CargoRes
format!(
"{} -> #{}",
removed[0],
- &added[0].source_id().precise().unwrap()[..8]
+ &added[0].source_id().precise_git_fragment().unwrap()
)
} else {
format!("{} -> v{}", removed[0], added[0].version())
};
- if removed[0].version() > added[0].version() {
+ // If versions differ only in build metadata, we call it an "update"
+ // regardless of whether the build metadata has gone up or down.
+ // This metadata is often stuff like git commit hashes, which are
+ // not meaningfully ordered.
+ if removed[0].version().cmp_precedence(added[0].version()) == Ordering::Greater {
print_change("Downgrading", msg, &style::WARN)?;
} else {
print_change("Updating", msg, &style::GOOD)?;
@@ -222,7 +230,7 @@ pub fn update_lockfile(ws: &Workspace<'_>, opts: &UpdateOptions<'_>) -> CargoRes
b[i..]
.iter()
.take_while(|b| a == b)
- .all(|b| a.source_id().precise() != b.source_id().precise())
+ .all(|b| !a.source_id().has_same_precise_as(b.source_id()))
})
.cloned()
.collect()
diff --git a/src/tools/cargo/src/cargo/ops/cargo_install.rs b/src/tools/cargo/src/cargo/ops/cargo_install.rs
index 957ab43e6..16027233e 100644
--- a/src/tools/cargo/src/cargo/ops/cargo_install.rs
+++ b/src/tools/cargo/src/cargo/ops/cargo_install.rs
@@ -68,6 +68,7 @@ impl<'cfg> InstallablePackage<'cfg> {
force: bool,
no_track: bool,
needs_update_if_source_is_index: bool,
+ current_rust_version: Option<&semver::Version>,
) -> CargoResult<Option<Self>> {
if let Some(name) = krate {
if name == "." {
@@ -105,6 +106,7 @@ impl<'cfg> InstallablePackage<'cfg> {
dep,
|git: &mut GitSource<'_>| git.read_packages(),
config,
+ current_rust_version,
)?
} else if source_id.is_path() {
let mut src = path_source(source_id, config)?;
@@ -142,6 +144,7 @@ impl<'cfg> InstallablePackage<'cfg> {
dep,
|path: &mut PathSource<'_>| path.read_packages(),
config,
+ current_rust_version,
)?
} else if let Some(dep) = dep {
let mut source = map.load(source_id, &HashSet::new())?;
@@ -161,7 +164,13 @@ impl<'cfg> InstallablePackage<'cfg> {
config.shell().status("Ignored", &msg)?;
return Ok(None);
}
- select_dep_pkg(&mut source, dep, config, needs_update_if_source_is_index)?
+ select_dep_pkg(
+ &mut source,
+ dep,
+ config,
+ needs_update_if_source_is_index,
+ current_rust_version,
+ )?
} else {
bail!(
"must specify a crate to install from \
@@ -616,6 +625,21 @@ pub fn install(
let dst = root.join("bin").into_path_unlocked();
let map = SourceConfigMap::new(config)?;
+ let current_rust_version = if opts.honor_rust_version {
+ let rustc = config.load_global_rustc(None)?;
+
+ // Remove any pre-release identifiers for easier comparison
+ let current_version = &rustc.version;
+ let untagged_version = semver::Version::new(
+ current_version.major,
+ current_version.minor,
+ current_version.patch,
+ );
+ Some(untagged_version)
+ } else {
+ None
+ };
+
let (installed_anything, scheduled_error) = if krates.len() <= 1 {
let (krate, vers) = krates
.iter()
@@ -623,7 +647,18 @@ pub fn install(
.map(|(k, v)| (Some(k.as_str()), v.as_ref()))
.unwrap_or((None, None));
let installable_pkg = InstallablePackage::new(
- config, root, map, krate, source_id, from_cwd, vers, opts, force, no_track, true,
+ config,
+ root,
+ map,
+ krate,
+ source_id,
+ from_cwd,
+ vers,
+ opts,
+ force,
+ no_track,
+ true,
+ current_rust_version.as_ref(),
)?;
let mut installed_anything = true;
if let Some(installable_pkg) = installable_pkg {
@@ -654,6 +689,7 @@ pub fn install(
force,
no_track,
!did_update,
+ current_rust_version.as_ref(),
) {
Ok(Some(installable_pkg)) => {
did_update = true;
@@ -773,7 +809,7 @@ where
// expensive network call in the case that the package is already installed.
// If this fails, the caller will possibly do an index update and try again, this is just a
// best-effort check to see if we can avoid hitting the network.
- if let Ok(pkg) = select_dep_pkg(source, dep, config, false) {
+ if let Ok(pkg) = select_dep_pkg(source, dep, config, false, None) {
let (_ws, rustc, target) =
make_ws_rustc_target(config, opts, &source.source_id(), pkg.clone())?;
if let Ok(true) = is_installed(&pkg, config, opts, &rustc, &target, root, dst, force) {
diff --git a/src/tools/cargo/src/cargo/ops/cargo_new.rs b/src/tools/cargo/src/cargo/ops/cargo_new.rs
index 78b3cc4f6..1c06b5f82 100644
--- a/src/tools/cargo/src/cargo/ops/cargo_new.rs
+++ b/src/tools/cargo/src/cargo/ops/cargo_new.rs
@@ -1,10 +1,11 @@
use crate::core::{Edition, Shell, Workspace};
use crate::util::errors::CargoResult;
use crate::util::important_paths::find_root_manifest_for_wd;
+use crate::util::toml_mut::is_sorted;
use crate::util::{existing_vcs_repo, FossilRepo, GitRepo, HgRepo, PijulRepo};
use crate::util::{restricted_names, Config};
-use anyhow::{anyhow, Context as _};
-use cargo_util::paths;
+use anyhow::{anyhow, Context};
+use cargo_util::paths::{self, write_atomic};
use serde::de;
use serde::Deserialize;
use std::collections::BTreeMap;
@@ -13,6 +14,7 @@ use std::io::{BufRead, BufReader, ErrorKind};
use std::path::{Path, PathBuf};
use std::str::FromStr;
use std::{fmt, slice};
+use toml_edit::{Array, Value};
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum VersionControl {
@@ -258,6 +260,12 @@ fn check_name(
name
))?;
}
+ let name_in_lowercase = name.to_lowercase();
+ if name != name_in_lowercase {
+ shell.warn(format!(
+ "the name `{name}` is not snake_case or kebab-case which is recommended for package names, consider `{name_in_lowercase}`"
+ ))?;
+ }
Ok(())
}
@@ -803,7 +811,7 @@ fn mk(config: &Config, opts: &MkOptions<'_>) -> CargoResult<()> {
// Sometimes the root manifest is not a valid manifest, so we only try to parse it if it is.
// This should not block the creation of the new project. It is only a best effort to
// inherit the workspace package keys.
- if let Ok(workspace_document) = root_manifest.parse::<toml_edit::Document>() {
+ if let Ok(mut workspace_document) = root_manifest.parse::<toml_edit::Document>() {
if let Some(workspace_package_keys) = workspace_document
.get("workspace")
.and_then(|workspace| workspace.get("package"))
@@ -826,6 +834,13 @@ fn mk(config: &Config, opts: &MkOptions<'_>) -> CargoResult<()> {
table["workspace"] = toml_edit::value(true);
manifest["lints"] = toml_edit::Item::Table(table);
}
+
+ // Try to add the new package to the workspace members.
+ update_manifest_with_new_member(
+ &root_manifest_path,
+ &mut workspace_document,
+ opts.path,
+ )?;
}
}
@@ -925,3 +940,95 @@ fn update_manifest_with_inherited_workspace_package_keys(
try_remove_and_inherit_package_key(key, manifest);
}
}
+
+/// Adds the new package member to the [workspace.members] array.
+/// - It first checks if the name matches any element in [workspace.exclude],
+/// and it ignores the name if there is a match.
+/// - Then it check if the name matches any element already in [workspace.members],
+/// and it ignores the name if there is a match.
+/// - If [workspace.members] doesn't exist in the manifest, it will add a new section
+/// with the new package in it.
+fn update_manifest_with_new_member(
+ root_manifest_path: &Path,
+ workspace_document: &mut toml_edit::Document,
+ package_path: &Path,
+) -> CargoResult<()> {
+ // Find the relative path for the package from the workspace root directory.
+ let workspace_root = root_manifest_path.parent().with_context(|| {
+ format!(
+ "workspace root manifest doesn't have a parent directory `{}`",
+ root_manifest_path.display()
+ )
+ })?;
+ let relpath = pathdiff::diff_paths(package_path, workspace_root).with_context(|| {
+ format!(
+ "path comparison requires two absolute paths; package_path: `{}`, workspace_path: `{}`",
+ package_path.display(),
+ workspace_root.display()
+ )
+ })?;
+
+ let mut components = Vec::new();
+ for comp in relpath.iter() {
+ let comp = comp.to_str().with_context(|| {
+ format!("invalid unicode component in path `{}`", relpath.display())
+ })?;
+ components.push(comp);
+ }
+ let display_path = components.join("/");
+
+ // Don't add the new package to the workspace's members
+ // if there is an exclusion match for it.
+ if let Some(exclude) = workspace_document
+ .get("workspace")
+ .and_then(|workspace| workspace.get("exclude"))
+ .and_then(|exclude| exclude.as_array())
+ {
+ for member in exclude {
+ let pat = member
+ .as_str()
+ .with_context(|| format!("invalid non-string exclude path `{}`", member))?;
+ if pat == display_path {
+ return Ok(());
+ }
+ }
+ }
+
+ // If the members element already exist, check if one of the patterns
+ // in the array already includes the new package's relative path.
+ // - Add the relative path if the members don't match the new package's path.
+ // - Create a new members array if there are no members element in the workspace yet.
+ if let Some(members) = workspace_document
+ .get_mut("workspace")
+ .and_then(|workspace| workspace.get_mut("members"))
+ .and_then(|members| members.as_array_mut())
+ {
+ for member in members.iter() {
+ let pat = member
+ .as_str()
+ .with_context(|| format!("invalid non-string member `{}`", member))?;
+ let pattern = glob::Pattern::new(pat)
+ .with_context(|| format!("cannot build glob pattern from `{}`", pat))?;
+
+ if pattern.matches(&display_path) {
+ return Ok(());
+ }
+ }
+
+ let was_sorted = is_sorted(members.iter().map(Value::as_str));
+ members.push(&display_path);
+ if was_sorted {
+ members.sort_by(|lhs, rhs| lhs.as_str().cmp(&rhs.as_str()));
+ }
+ } else {
+ let mut array = Array::new();
+ array.push(&display_path);
+
+ workspace_document["workspace"]["members"] = toml_edit::value(array);
+ }
+
+ write_atomic(
+ &root_manifest_path,
+ workspace_document.to_string().to_string().as_bytes(),
+ )
+}
diff --git a/src/tools/cargo/src/cargo/ops/cargo_package.rs b/src/tools/cargo/src/cargo/ops/cargo_package.rs
index b6423aa96..6ac09dc77 100644
--- a/src/tools/cargo/src/cargo/ops/cargo_package.rs
+++ b/src/tools/cargo/src/cargo/ops/cargo_package.rs
@@ -3,7 +3,6 @@ use std::fs::{self, File};
use std::io::prelude::*;
use std::io::SeekFrom;
use std::path::{Path, PathBuf};
-use std::rc::Rc;
use std::sync::Arc;
use std::task::Poll;
@@ -13,9 +12,10 @@ use crate::core::{registry::PackageRegistry, resolver::HasDevUnits};
use crate::core::{Feature, Shell, Verbosity, Workspace};
use crate::core::{Package, PackageId, PackageSet, Resolve, SourceId};
use crate::sources::PathSource;
+use crate::util::cache_lock::CacheLockMode;
use crate::util::config::JobsConfig;
use crate::util::errors::CargoResult;
-use crate::util::toml::TomlManifest;
+use crate::util::toml::schema::TomlManifest;
use crate::util::{self, human_readable_bytes, restricted_names, Config, FileLock};
use crate::{drop_println, ops};
use anyhow::Context as _;
@@ -132,7 +132,7 @@ pub fn package_one(
let dir = ws.target_dir().join("package");
let mut dst = {
let tmp = format!(".{}", filename);
- dir.open_rw(&tmp, config, "package scratch space")?
+ dir.open_rw_exclusive_create(&tmp, config, "package scratch space")?
};
// Package up and test a temporary tarball and only move it to the final
@@ -411,16 +411,14 @@ fn build_lock(ws: &Workspace<'_>, orig_pkg: &Package) -> CargoResult<String> {
let orig_resolve = ops::load_pkg_lockfile(ws)?;
// Convert Package -> TomlManifest -> Manifest -> Package
- let toml_manifest = Rc::new(
- orig_pkg
- .manifest()
- .original()
- .prepare_for_publish(ws, orig_pkg.root())?,
- );
+ let toml_manifest = orig_pkg
+ .manifest()
+ .original()
+ .prepare_for_publish(ws, orig_pkg.root())?;
let package_root = orig_pkg.root();
let source_id = orig_pkg.package_id().source_id();
let (manifest, _nested_paths) =
- TomlManifest::to_real_manifest(&toml_manifest, false, source_id, package_root, config)?;
+ TomlManifest::to_real_manifest(toml_manifest, false, source_id, package_root, config)?;
let new_pkg = Package::new(manifest, orig_pkg.manifest_path());
let max_rust_version = new_pkg.rust_version().cloned();
@@ -806,7 +804,7 @@ pub fn check_yanked(
) -> CargoResult<()> {
// Checking the yanked status involves taking a look at the registry and
// maybe updating files, so be sure to lock it here.
- let _lock = config.acquire_package_cache_lock()?;
+ let _lock = config.acquire_package_cache_lock(CacheLockMode::DownloadExclusive)?;
let mut sources = pkg_set.sources_mut();
let mut pending: Vec<PackageId> = resolve.iter().collect();
diff --git a/src/tools/cargo/src/cargo/ops/cargo_uninstall.rs b/src/tools/cargo/src/cargo/ops/cargo_uninstall.rs
index 355154418..1f22e191e 100644
--- a/src/tools/cargo/src/cargo/ops/cargo_uninstall.rs
+++ b/src/tools/cargo/src/cargo/ops/cargo_uninstall.rs
@@ -90,6 +90,7 @@ fn uninstall_cwd(root: &Filesystem, bins: &[String], config: &Config) -> CargoRe
None,
|path: &mut PathSource<'_>| path.read_packages(),
config,
+ None,
)?;
let pkgid = pkg.package_id();
uninstall_pkgid(root, tracker, pkgid, bins, config)
diff --git a/src/tools/cargo/src/cargo/ops/common_for_install_and_uninstall.rs b/src/tools/cargo/src/cargo/ops/common_for_install_and_uninstall.rs
index 0934cbd29..d1f9152be 100644
--- a/src/tools/cargo/src/cargo/ops/common_for_install_and_uninstall.rs
+++ b/src/tools/cargo/src/cargo/ops/common_for_install_and_uninstall.rs
@@ -17,6 +17,7 @@ use crate::ops::{self, CompileFilter, CompileOptions};
use crate::sources::source::QueryKind;
use crate::sources::source::Source;
use crate::sources::PathSource;
+use crate::util::cache_lock::CacheLockMode;
use crate::util::errors::CargoResult;
use crate::util::Config;
use crate::util::{FileLock, Filesystem};
@@ -97,8 +98,10 @@ pub struct CrateListingV1 {
impl InstallTracker {
/// Create an InstallTracker from information on disk.
pub fn load(config: &Config, root: &Filesystem) -> CargoResult<InstallTracker> {
- let v1_lock = root.open_rw(Path::new(".crates.toml"), config, "crate metadata")?;
- let v2_lock = root.open_rw(Path::new(".crates2.json"), config, "crate metadata")?;
+ let v1_lock =
+ root.open_rw_exclusive_create(Path::new(".crates.toml"), config, "crate metadata")?;
+ let v2_lock =
+ root.open_rw_exclusive_create(Path::new(".crates2.json"), config, "crate metadata")?;
let v1 = (|| -> CargoResult<_> {
let mut contents = String::new();
@@ -212,7 +215,7 @@ impl InstallTracker {
let precise_equal = if source_id.is_git() {
// Git sources must have the exact same hash to be
// considered "fresh".
- dupe_pkg_id.source_id().precise() == source_id.precise()
+ dupe_pkg_id.source_id().has_same_precise_as(source_id)
} else {
true
};
@@ -529,6 +532,7 @@ pub fn select_dep_pkg<T>(
dep: Dependency,
config: &Config,
needs_update: bool,
+ current_rust_version: Option<&semver::Version>,
) -> CargoResult<Package>
where
T: Source,
@@ -536,7 +540,7 @@ where
// This operation may involve updating some sources or making a few queries
// which may involve frobbing caches, as a result make sure we synchronize
// with other global Cargos
- let _lock = config.acquire_package_cache_lock()?;
+ let _lock = config.acquire_package_cache_lock(CacheLockMode::DownloadExclusive)?;
if needs_update {
source.invalidate_cache();
@@ -548,9 +552,56 @@ where
Poll::Pending => source.block_until_ready()?,
}
};
- match deps.iter().map(|p| p.package_id()).max() {
- Some(pkgid) => {
- let pkg = Box::new(source).download_now(pkgid, config)?;
+ match deps.iter().max_by_key(|p| p.package_id()) {
+ Some(summary) => {
+ if let (Some(current), Some(msrv)) = (current_rust_version, summary.rust_version()) {
+ let msrv_req = msrv.to_caret_req();
+ if !msrv_req.matches(current) {
+ let name = summary.name();
+ let ver = summary.version();
+ let extra = if dep.source_id().is_registry() {
+ // Match any version, not just the selected
+ let msrv_dep =
+ Dependency::parse(dep.package_name(), None, dep.source_id())?;
+ let msrv_deps = loop {
+ match source.query_vec(&msrv_dep, QueryKind::Exact)? {
+ Poll::Ready(deps) => break deps,
+ Poll::Pending => source.block_until_ready()?,
+ }
+ };
+ if let Some(alt) = msrv_deps
+ .iter()
+ .filter(|summary| {
+ summary
+ .rust_version()
+ .map(|msrv| msrv.to_caret_req().matches(current))
+ .unwrap_or(true)
+ })
+ .max_by_key(|s| s.package_id())
+ {
+ if let Some(rust_version) = alt.rust_version() {
+ format!(
+ "\n`{name} {}` supports rustc {rust_version}",
+ alt.version()
+ )
+ } else {
+ format!(
+ "\n`{name} {}` has an unspecified minimum rustc version",
+ alt.version()
+ )
+ }
+ } else {
+ String::new()
+ }
+ } else {
+ String::new()
+ };
+ bail!("\
+cannot install package `{name} {ver}`, it requires rustc {msrv} or newer, while the currently active rustc version is {current}{extra}"
+)
+ }
+ }
+ let pkg = Box::new(source).download_now(summary.package_id(), config)?;
Ok(pkg)
}
None => {
@@ -596,6 +647,7 @@ pub fn select_pkg<T, F>(
dep: Option<Dependency>,
mut list_all: F,
config: &Config,
+ current_rust_version: Option<&semver::Version>,
) -> CargoResult<Package>
where
T: Source,
@@ -604,12 +656,12 @@ where
// This operation may involve updating some sources or making a few queries
// which may involve frobbing caches, as a result make sure we synchronize
// with other global Cargos
- let _lock = config.acquire_package_cache_lock()?;
+ let _lock = config.acquire_package_cache_lock(CacheLockMode::DownloadExclusive)?;
source.invalidate_cache();
return if let Some(dep) = dep {
- select_dep_pkg(source, dep, config, false)
+ select_dep_pkg(source, dep, config, false, current_rust_version)
} else {
let candidates = list_all(source)?;
let binaries = candidates
diff --git a/src/tools/cargo/src/cargo/ops/fix.rs b/src/tools/cargo/src/cargo/ops/fix.rs
index 9d6459294..6630914a4 100644
--- a/src/tools/cargo/src/cargo/ops/fix.rs
+++ b/src/tools/cargo/src/cargo/ops/fix.rs
@@ -451,6 +451,11 @@ pub fn fix_exec_rustc(config: &Config, lock_addr: &str) -> CargoResult<()> {
// things like colored output to work correctly.
rustc.arg(arg);
}
+ // Removes `FD_CLOEXEC` set by `jobserver::Client` to pass jobserver
+ // as environment variables specify.
+ if let Some(client) = config.jobserver_from_env() {
+ rustc.inherit_jobserver(client);
+ }
debug!("calling rustc to display remaining diagnostics: {rustc}");
exit_with(rustc.status()?);
}
diff --git a/src/tools/cargo/src/cargo/ops/lockfile.rs b/src/tools/cargo/src/cargo/ops/lockfile.rs
index 26456e560..2160b6f01 100644
--- a/src/tools/cargo/src/cargo/ops/lockfile.rs
+++ b/src/tools/cargo/src/cargo/ops/lockfile.rs
@@ -12,7 +12,7 @@ pub fn load_pkg_lockfile(ws: &Workspace<'_>) -> CargoResult<Option<Resolve>> {
return Ok(None);
}
- let mut f = lock_root.open_ro("Cargo.lock", ws.config(), "Cargo.lock file")?;
+ let mut f = lock_root.open_ro_shared("Cargo.lock", ws.config(), "Cargo.lock file")?;
let mut s = String::new();
f.read_to_string(&mut s)
@@ -64,22 +64,21 @@ pub fn write_pkg_lockfile(ws: &Workspace<'_>, resolve: &mut Resolve) -> CargoRes
// out lock file updates as they're otherwise already updated, and changes
// which don't touch dependencies won't seemingly spuriously update the lock
// file.
- if resolve.version() < ResolveVersion::default() {
- resolve.set_version(ResolveVersion::default());
+ let default_version = ResolveVersion::default();
+ let current_version = resolve.version();
+ let next_lockfile_bump = ws.config().cli_unstable().next_lockfile_bump;
+
+ if current_version < default_version {
+ resolve.set_version(default_version);
out = serialize_resolve(resolve, orig.as_deref());
- } else if resolve.version() > ResolveVersion::default()
- && !ws.config().cli_unstable().next_lockfile_bump
- {
+ } else if current_version > ResolveVersion::max_stable() && !next_lockfile_bump {
// The next version hasn't yet stabilized.
- anyhow::bail!(
- "lock file version `{:?}` requires `-Znext-lockfile-bump`",
- resolve.version()
- )
+ anyhow::bail!("lock file version `{current_version:?}` requires `-Znext-lockfile-bump`")
}
// Ok, if that didn't work just write it out
lock_root
- .open_rw("Cargo.lock", ws.config(), "Cargo.lock file")
+ .open_rw_exclusive_create("Cargo.lock", ws.config(), "Cargo.lock file")
.and_then(|mut f| {
f.file().set_len(0)?;
f.write_all(out.as_bytes())?;
@@ -100,7 +99,7 @@ fn resolve_to_string_orig(
) -> (Option<String>, String, Filesystem) {
// Load the original lock file if it exists.
let lock_root = lock_root(ws);
- let orig = lock_root.open_ro("Cargo.lock", ws.config(), "Cargo.lock file");
+ let orig = lock_root.open_ro_shared("Cargo.lock", ws.config(), "Cargo.lock file");
let orig = orig.and_then(|mut f| {
let mut s = String::new();
f.read_to_string(&mut s)?;
diff --git a/src/tools/cargo/src/cargo/ops/registry/mod.rs b/src/tools/cargo/src/cargo/ops/registry/mod.rs
index c82230a16..3fa8fd291 100644
--- a/src/tools/cargo/src/cargo/ops/registry/mod.rs
+++ b/src/tools/cargo/src/cargo/ops/registry/mod.rs
@@ -22,6 +22,7 @@ use crate::core::SourceId;
use crate::sources::source::Source;
use crate::sources::{RegistrySource, SourceConfigMap};
use crate::util::auth;
+use crate::util::cache_lock::CacheLockMode;
use crate::util::config::{Config, PathAndArgs};
use crate::util::errors::CargoResult;
use crate::util::network::http::http_handle;
@@ -131,7 +132,7 @@ fn registry(
}
let cfg = {
- let _lock = config.acquire_package_cache_lock()?;
+ let _lock = config.acquire_package_cache_lock(CacheLockMode::DownloadExclusive)?;
let mut src = RegistrySource::remote(source_ids.replacement, &HashSet::new(), config)?;
// Only update the index if `force_update` is set.
if force_update {
diff --git a/src/tools/cargo/src/cargo/ops/registry/publish.rs b/src/tools/cargo/src/cargo/ops/registry/publish.rs
index a88a0a30f..201907bb2 100644
--- a/src/tools/cargo/src/cargo/ops/registry/publish.rs
+++ b/src/tools/cargo/src/cargo/ops/registry/publish.rs
@@ -30,6 +30,7 @@ use crate::sources::source::QueryKind;
use crate::sources::SourceConfigMap;
use crate::sources::CRATES_IO_REGISTRY;
use crate::util::auth;
+use crate::util::cache_lock::CacheLockMode;
use crate::util::config::JobsConfig;
use crate::util::Progress;
use crate::util::ProgressStyle;
@@ -102,7 +103,7 @@ pub fn publish(ws: &Workspace<'_>, opts: &PublishOpts<'_>) -> CargoResult<()> {
if allowed_registries.is_empty() {
bail!(
"`{}` cannot be published.\n\
- `package.publish` is set to `false` or an empty list in Cargo.toml and prevents publishing.",
+ `package.publish` must be set to `true` or a non-empty list in Cargo.toml to publish.",
pkg.name(),
);
} else if !allowed_registries.contains(&reg_name) {
@@ -233,7 +234,7 @@ fn wait_for_publish(
progress.tick_now(0, max, "")?;
let is_available = loop {
{
- let _lock = config.acquire_package_cache_lock()?;
+ let _lock = config.acquire_package_cache_lock(CacheLockMode::DownloadExclusive)?;
// Force re-fetching the source
//
// As pulling from a git source is expensive, we track when we've done it within the
diff --git a/src/tools/cargo/src/cargo/ops/registry/search.rs b/src/tools/cargo/src/cargo/ops/registry/search.rs
index 10b4d600e..0f4649754 100644
--- a/src/tools/cargo/src/cargo/ops/registry/search.rs
+++ b/src/tools/cargo/src/cargo/ops/registry/search.rs
@@ -3,7 +3,6 @@
//! [1]: https://doc.rust-lang.org/nightly/cargo/reference/registry-web-api.html#search
use std::cmp;
-use std::iter::repeat;
use anyhow::Context as _;
use url::Url;
@@ -35,7 +34,7 @@ pub fn search(
.map(|krate| format!("{} = \"{}\"", krate.name, krate.max_version))
.collect::<Vec<String>>();
- let description_margin = names.iter().map(|s| s.len() + 4).max().unwrap_or_default();
+ let description_margin = names.iter().map(|s| s.len()).max().unwrap_or_default() + 4;
let description_length = cmp::max(80, 128 - description_margin);
@@ -46,34 +45,32 @@ pub fn search(
.map(|desc| truncate_with_ellipsis(&desc.replace("\n", " "), description_length))
});
+ let mut shell = config.shell();
+ let stdout = shell.out();
+ let good = style::GOOD.render();
+ let reset = anstyle::Reset.render();
+
for (name, description) in names.into_iter().zip(descriptions) {
let line = match description {
- Some(desc) => {
- let space = repeat(' ')
- .take(description_margin - name.len())
- .collect::<String>();
- name + &space + "# " + &desc
- }
+ Some(desc) => format!("{name: <description_margin$}# {desc}"),
None => name,
};
let mut fragments = line.split(query).peekable();
while let Some(fragment) = fragments.next() {
- let _ = config.shell().write_stdout(fragment, &style::NOP);
+ let _ = write!(stdout, "{fragment}");
if fragments.peek().is_some() {
- let _ = config.shell().write_stdout(query, &style::GOOD);
+ let _ = write!(stdout, "{good}{query}{reset}");
}
}
- let _ = config.shell().write_stdout("\n", &style::NOP);
+ let _ = writeln!(stdout);
}
let search_max_limit = 100;
if total_crates > limit && limit < search_max_limit {
- let _ = config.shell().write_stdout(
- format_args!(
- "... and {} crates more (use --limit N to see more)\n",
- total_crates - limit
- ),
- &style::NOP,
+ let _ = writeln!(
+ stdout,
+ "... and {} crates more (use --limit N to see more)",
+ total_crates - limit
);
} else if total_crates > limit && limit >= search_max_limit {
let extra = if source_ids.original.is_crates_io() {
@@ -82,9 +79,11 @@ pub fn search(
} else {
String::new()
};
- let _ = config.shell().write_stdout(
- format_args!("... and {} crates more{}\n", total_crates - limit, extra),
- &style::NOP,
+ let _ = writeln!(
+ stdout,
+ "... and {} crates more{}",
+ total_crates - limit,
+ extra
);
}
diff --git a/src/tools/cargo/src/cargo/ops/resolve.rs b/src/tools/cargo/src/cargo/ops/resolve.rs
index 053098d55..8ca72f77c 100644
--- a/src/tools/cargo/src/cargo/ops/resolve.rs
+++ b/src/tools/cargo/src/cargo/ops/resolve.rs
@@ -61,13 +61,14 @@ use crate::core::resolver::features::{
CliFeatures, FeatureOpts, FeatureResolver, ForceAllTargets, RequestedFeatures, ResolvedFeatures,
};
use crate::core::resolver::{
- self, HasDevUnits, Resolve, ResolveOpts, ResolveVersion, VersionPreferences,
+ self, HasDevUnits, Resolve, ResolveOpts, ResolveVersion, VersionOrdering, VersionPreferences,
};
use crate::core::summary::Summary;
use crate::core::Feature;
use crate::core::{GitReference, PackageId, PackageIdSpec, PackageSet, SourceId, Workspace};
use crate::ops;
use crate::sources::PathSource;
+use crate::util::cache_lock::CacheLockMode;
use crate::util::errors::CargoResult;
use crate::util::RustVersion;
use crate::util::{profile, CanonicalUrl};
@@ -289,7 +290,9 @@ pub fn resolve_with_previous<'cfg>(
) -> CargoResult<Resolve> {
// We only want one Cargo at a time resolving a crate graph since this can
// involve a lot of frobbing of the global caches.
- let _lock = ws.config().acquire_package_cache_lock()?;
+ let _lock = ws
+ .config()
+ .acquire_package_cache_lock(CacheLockMode::DownloadExclusive)?;
// Here we place an artificial limitation that all non-registry sources
// cannot be locked at more than one revision. This means that if a Git
@@ -318,6 +321,12 @@ pub fn resolve_with_previous<'cfg>(
// While registering patches, we will record preferences for particular versions
// of various packages.
let mut version_prefs = VersionPreferences::default();
+ if ws.config().cli_unstable().minimal_versions {
+ version_prefs.version_ordering(VersionOrdering::MinimumVersionsFirst)
+ }
+ if ws.config().cli_unstable().msrv_policy {
+ version_prefs.max_rust_version(max_rust_version.cloned());
+ }
// This is a set of PackageIds of `[patch]` entries, and some related locked PackageIds, for
// which locking should be avoided (but which will be preferred when searching dependencies,
@@ -386,12 +395,8 @@ pub fn resolve_with_previous<'cfg>(
}) {
Some(id_using_default) => {
let id_using_master = id_using_default.with_source_id(
- dep.source_id().with_precise(
- id_using_default
- .source_id()
- .precise()
- .map(|s| s.to_string()),
- ),
+ dep.source_id()
+ .with_precise_from(id_using_default.source_id()),
);
let mut locked_dep = dep.clone();
@@ -510,7 +515,6 @@ pub fn resolve_with_previous<'cfg>(
ws.unstable_features()
.require(Feature::public_dependency())
.is_ok(),
- max_rust_version,
)?;
let patches: Vec<_> = registry
.patches()
@@ -793,7 +797,7 @@ fn master_branch_git_source(id: PackageId, resolve: &Resolve) -> Option<PackageI
let new_source =
SourceId::for_git(source.url(), GitReference::Branch("master".to_string()))
.unwrap()
- .with_precise(source.precise().map(|s| s.to_string()));
+ .with_precise_from(source);
return Some(id.with_source_id(new_source));
}
}
diff --git a/src/tools/cargo/src/cargo/ops/vendor.rs b/src/tools/cargo/src/cargo/ops/vendor.rs
index 3ee46db32..cad7fc5d1 100644
--- a/src/tools/cargo/src/cargo/ops/vendor.rs
+++ b/src/tools/cargo/src/cargo/ops/vendor.rs
@@ -258,7 +258,7 @@ fn sync(
} else {
// Remove `precise` since that makes the source name very long,
// and isn't needed to disambiguate multiple sources.
- source_id.with_precise(None).as_url().to_string()
+ source_id.without_precise().as_url().to_string()
};
let source = if source_id.is_crates_io() {
diff --git a/src/tools/cargo/src/cargo/sources/config.rs b/src/tools/cargo/src/cargo/sources/config.rs
index cc7aa9925..4a0332eca 100644
--- a/src/tools/cargo/src/cargo/sources/config.rs
+++ b/src/tools/cargo/src/cargo/sources/config.rs
@@ -145,7 +145,7 @@ impl<'cfg> SourceConfigMap<'cfg> {
// Attempt to interpret the source name as an alt registry name
if let Ok(alt_id) = SourceId::alt_registry(self.config, name) {
debug!("following pointer to registry {}", name);
- break alt_id.with_precise(id.precise().map(str::to_string));
+ break alt_id.with_precise_from(id);
}
bail!(
"could not find a configured source with the \
@@ -163,7 +163,7 @@ impl<'cfg> SourceConfigMap<'cfg> {
}
None if id == cfg.id => return id.load(self.config, yanked_whitelist),
None => {
- break cfg.id.with_precise(id.precise().map(|s| s.to_string()));
+ break cfg.id.with_precise_from(id);
}
}
debug!("following pointer to {}", name);
@@ -199,7 +199,7 @@ a lock file compatible with `{orig}` cannot be generated in this situation
);
}
- if old_src.requires_precise() && id.precise().is_none() {
+ if old_src.requires_precise() && !id.has_precise() {
bail!(
"\
the source {orig} requires a lock file to be present first before it can be
diff --git a/src/tools/cargo/src/cargo/sources/git/source.rs b/src/tools/cargo/src/cargo/sources/git/source.rs
index 7af342f45..a75c1ec6d 100644
--- a/src/tools/cargo/src/cargo/sources/git/source.rs
+++ b/src/tools/cargo/src/cargo/sources/git/source.rs
@@ -8,6 +8,7 @@ use crate::sources::source::MaybePackage;
use crate::sources::source::QueryKind;
use crate::sources::source::Source;
use crate::sources::PathSource;
+use crate::util::cache_lock::CacheLockMode;
use crate::util::errors::CargoResult;
use crate::util::hex::short_hash;
use crate::util::Config;
@@ -88,13 +89,7 @@ impl<'cfg> GitSource<'cfg> {
let remote = GitRemote::new(source_id.url());
let manifest_reference = source_id.git_reference().unwrap().clone();
- let locked_rev =
- match source_id.precise() {
- Some(s) => Some(git2::Oid::from_str(s).with_context(|| {
- format!("precise value for git is not a git revision: {}", s)
- })?),
- None => None,
- };
+ let locked_rev = source_id.precise_git_oid()?;
let ident = ident_shallow(
&source_id,
config
@@ -212,7 +207,9 @@ impl<'cfg> Source for GitSource<'cfg> {
// Ignore errors creating it, in case this is a read-only filesystem:
// perhaps the later operations can succeed anyhow.
let _ = git_fs.create_dir();
- let git_path = self.config.assert_package_cache_locked(&git_fs);
+ let git_path = self
+ .config
+ .assert_package_cache_locked(CacheLockMode::DownloadExclusive, &git_fs);
// Before getting a checkout, make sure that `<cargo_home>/git` is
// marked as excluded from indexing and backups. Older versions of Cargo
@@ -287,7 +284,9 @@ impl<'cfg> Source for GitSource<'cfg> {
.join(short_id.as_str());
db.copy_to(actual_rev, &checkout_path, self.config)?;
- let source_id = self.source_id.with_precise(Some(actual_rev.to_string()));
+ let source_id = self
+ .source_id
+ .with_git_precise(Some(actual_rev.to_string()));
let path_source = PathSource::new_recursive(&checkout_path, source_id, self.config);
self.path_source = Some(path_source);
diff --git a/src/tools/cargo/src/cargo/sources/registry/download.rs b/src/tools/cargo/src/cargo/sources/registry/download.rs
index 9bf838625..786432835 100644
--- a/src/tools/cargo/src/cargo/sources/registry/download.rs
+++ b/src/tools/cargo/src/cargo/sources/registry/download.rs
@@ -12,6 +12,7 @@ use crate::core::PackageId;
use crate::sources::registry::MaybeLock;
use crate::sources::registry::RegistryConfig;
use crate::util::auth;
+use crate::util::cache_lock::CacheLockMode;
use crate::util::errors::CargoResult;
use crate::util::{Config, Filesystem};
use std::fmt::Write as FmtWrite;
@@ -38,7 +39,7 @@ pub(super) fn download(
registry_config: RegistryConfig,
) -> CargoResult<MaybeLock> {
let path = cache_path.join(&pkg.tarball_name());
- let path = config.assert_package_cache_locked(&path);
+ let path = config.assert_package_cache_locked(CacheLockMode::DownloadExclusive, &path);
// Attempt to open a read-only copy first to avoid an exclusive write
// lock and also work with read-only filesystems. Note that we check the
@@ -117,7 +118,7 @@ pub(super) fn finish_download(
cache_path.create_dir()?;
let path = cache_path.join(&pkg.tarball_name());
- let path = config.assert_package_cache_locked(&path);
+ let path = config.assert_package_cache_locked(CacheLockMode::DownloadExclusive, &path);
let mut dst = OpenOptions::new()
.create(true)
.read(true)
@@ -144,7 +145,7 @@ pub(super) fn is_crate_downloaded(
pkg: PackageId,
) -> bool {
let path = cache_path.join(pkg.tarball_name());
- let path = config.assert_package_cache_locked(&path);
+ let path = config.assert_package_cache_locked(CacheLockMode::DownloadExclusive, &path);
if let Ok(meta) = fs::metadata(path) {
return meta.len() > 0;
}
diff --git a/src/tools/cargo/src/cargo/sources/registry/http_remote.rs b/src/tools/cargo/src/cargo/sources/registry/http_remote.rs
index 9fe76333b..3d31110c3 100644
--- a/src/tools/cargo/src/cargo/sources/registry/http_remote.rs
+++ b/src/tools/cargo/src/cargo/sources/registry/http_remote.rs
@@ -4,6 +4,7 @@ use crate::core::{PackageId, SourceId};
use crate::sources::registry::download;
use crate::sources::registry::MaybeLock;
use crate::sources::registry::{LoadResponse, RegistryConfig, RegistryData};
+use crate::util::cache_lock::CacheLockMode;
use crate::util::errors::{CargoResult, HttpNotSuccessful};
use crate::util::network::http::http_handle;
use crate::util::network::retry::{Retry, RetryResult};
@@ -461,7 +462,8 @@ impl<'cfg> RegistryData for HttpRegistry<'cfg> {
}
fn assert_index_locked<'a>(&self, path: &'a Filesystem) -> &'a Path {
- self.config.assert_package_cache_locked(path)
+ self.config
+ .assert_package_cache_locked(CacheLockMode::DownloadExclusive, path)
}
fn is_updated(&self) -> bool {
@@ -744,6 +746,7 @@ impl<'cfg> RegistryData for HttpRegistry<'cfg> {
Poll::Ready(cfg) => break cfg.to_owned(),
}
};
+
download::download(
&self.cache_path,
&self.config,
diff --git a/src/tools/cargo/src/cargo/sources/registry/index.rs b/src/tools/cargo/src/cargo/sources/registry/index.rs
index ca1cf4069..00f21d669 100644
--- a/src/tools/cargo/src/cargo/sources/registry/index.rs
+++ b/src/tools/cargo/src/cargo/sources/registry/index.rs
@@ -89,6 +89,7 @@ use crate::core::dependency::{Artifact, DepKind};
use crate::core::Dependency;
use crate::core::{PackageId, SourceId, Summary};
use crate::sources::registry::{LoadResponse, RegistryData};
+use crate::util::cache_lock::CacheLockMode;
use crate::util::interning::InternedString;
use crate::util::IntoUrl;
use crate::util::{internal, CargoResult, Config, Filesystem, OptVersionReq, RustVersion};
@@ -98,7 +99,7 @@ use semver::Version;
use serde::Deserialize;
use std::borrow::Cow;
use std::collections::BTreeMap;
-use std::collections::{HashMap, HashSet};
+use std::collections::HashMap;
use std::fs;
use std::io::ErrorKind;
use std::path::Path;
@@ -437,11 +438,9 @@ impl<'cfg> RegistryIndex<'cfg> {
/// checking the integrity of a downloaded package matching the checksum in
/// the index file, aka [`IndexSummary`].
pub fn hash(&mut self, pkg: PackageId, load: &mut dyn RegistryData) -> Poll<CargoResult<&str>> {
- let req = OptVersionReq::exact(pkg.version());
+ let req = OptVersionReq::lock_to_exact(pkg.version());
let summary = self.summaries(pkg.name(), &req, load)?;
- let summary = ready!(summary)
- .filter(|s| s.package_id().version() == pkg.version())
- .next();
+ let summary = ready!(summary).next();
Poll::Ready(Ok(summary
.ok_or_else(|| internal(format!("no hash listed for {}", pkg)))?
.as_summary()
@@ -574,8 +573,7 @@ impl<'cfg> RegistryIndex<'cfg> {
name: InternedString,
req: &OptVersionReq,
load: &mut dyn RegistryData,
- yanked_whitelist: &HashSet<PackageId>,
- f: &mut dyn FnMut(Summary),
+ f: &mut dyn FnMut(IndexSummary),
) -> Poll<CargoResult<()>> {
if self.config.offline() {
// This should only return `Poll::Ready(Ok(()))` if there is at least 1 match.
@@ -592,31 +590,15 @@ impl<'cfg> RegistryIndex<'cfg> {
let callback = &mut |s: IndexSummary| {
if !s.is_offline() {
called = true;
- f(s.into_summary());
+ f(s);
}
};
- ready!(self.query_inner_with_online(
- name,
- req,
- load,
- yanked_whitelist,
- callback,
- false
- )?);
+ ready!(self.query_inner_with_online(name, req, load, callback, false)?);
if called {
return Poll::Ready(Ok(()));
}
}
- self.query_inner_with_online(
- name,
- req,
- load,
- yanked_whitelist,
- &mut |s| {
- f(s.into_summary());
- },
- true,
- )
+ self.query_inner_with_online(name, req, load, f, true)
}
/// Inner implementation of [`Self::query_inner`]. Returns the number of
@@ -628,15 +610,10 @@ impl<'cfg> RegistryIndex<'cfg> {
name: InternedString,
req: &OptVersionReq,
load: &mut dyn RegistryData,
- yanked_whitelist: &HashSet<PackageId>,
f: &mut dyn FnMut(IndexSummary),
online: bool,
) -> Poll<CargoResult<()>> {
- let source_id = self.source_id;
-
- let summaries = ready!(self.summaries(name, req, load))?;
-
- let summaries = summaries
+ ready!(self.summaries(name, &req, load))?
// First filter summaries for `--offline`. If we're online then
// everything is a candidate, otherwise if we're offline we're only
// going to consider candidates which are actually present on disk.
@@ -654,41 +631,7 @@ impl<'cfg> RegistryIndex<'cfg> {
IndexSummary::Offline(s.as_summary().clone())
}
})
- // Next filter out all yanked packages. Some yanked packages may
- // leak through if they're in a whitelist (aka if they were
- // previously in `Cargo.lock`
- .filter(|s| !s.is_yanked() || yanked_whitelist.contains(&s.package_id()));
-
- // Handle `cargo update --precise` here.
- let precise = source_id.precise_registry_version(name.as_str());
- let summaries = summaries.filter(|s| match &precise {
- Some((current, requested)) => {
- if req.matches(current) {
- // Unfortunately crates.io allows versions to differ only
- // by build metadata. This shouldn't be allowed, but since
- // it is, this will honor it if requested. However, if not
- // specified, then ignore it.
- let s_vers = s.package_id().version();
- match (s_vers.build.is_empty(), requested.build.is_empty()) {
- (true, true) => s_vers == requested,
- (true, false) => false,
- (false, true) => {
- // Strip out the metadata.
- s_vers.major == requested.major
- && s_vers.minor == requested.minor
- && s_vers.patch == requested.patch
- && s_vers.pre == requested.pre
- }
- (false, false) => s_vers == requested,
- }
- } else {
- true
- }
- }
- None => true,
- });
-
- summaries.for_each(f);
+ .for_each(f);
Poll::Ready(Ok(()))
}
@@ -698,10 +641,8 @@ impl<'cfg> RegistryIndex<'cfg> {
pkg: PackageId,
load: &mut dyn RegistryData,
) -> Poll<CargoResult<bool>> {
- let req = OptVersionReq::exact(pkg.version());
- let found = ready!(self.summaries(pkg.name(), &req, load))?
- .filter(|s| s.package_id().version() == pkg.version())
- .any(|s| s.is_yanked());
+ let req = OptVersionReq::lock_to_exact(pkg.version());
+ let found = ready!(self.summaries(pkg.name(), &req, load))?.any(|s| s.is_yanked());
Poll::Ready(Ok(found))
}
}
@@ -823,7 +764,7 @@ impl Summaries {
// something in case of error.
if paths::create_dir_all(cache_path.parent().unwrap()).is_ok() {
let path = Filesystem::new(cache_path.clone());
- config.assert_package_cache_locked(&path);
+ config.assert_package_cache_locked(CacheLockMode::DownloadExclusive, &path);
if let Err(e) = fs::write(cache_path, &cache_bytes) {
tracing::info!("failed to write cache: {}", e);
}
@@ -994,7 +935,7 @@ impl IndexSummary {
} = serde_json::from_slice(line)?;
let v = v.unwrap_or(1);
tracing::trace!("json parsed registry {}/{}", name, vers);
- let pkgid = PackageId::new(name, &vers, source_id)?;
+ let pkgid = PackageId::pure(name.into(), vers.clone(), source_id);
let deps = deps
.into_iter()
.map(|dep| dep.into_dep(source_id))
diff --git a/src/tools/cargo/src/cargo/sources/registry/mod.rs b/src/tools/cargo/src/cargo/sources/registry/mod.rs
index fb8f79817..7ee461edd 100644
--- a/src/tools/cargo/src/cargo/sources/registry/mod.rs
+++ b/src/tools/cargo/src/cargo/sources/registry/mod.rs
@@ -206,6 +206,7 @@ use crate::sources::source::MaybePackage;
use crate::sources::source::QueryKind;
use crate::sources::source::Source;
use crate::sources::PathSource;
+use crate::util::cache_lock::CacheLockMode;
use crate::util::hex;
use crate::util::interning::InternedString;
use crate::util::network::PollExt;
@@ -581,7 +582,9 @@ impl<'cfg> RegistrySource<'cfg> {
let package_dir = format!("{}-{}", pkg.name(), pkg.version());
let dst = self.src_path.join(&package_dir);
let path = dst.join(PACKAGE_SOURCE_LOCK);
- let path = self.config.assert_package_cache_locked(&path);
+ let path = self
+ .config
+ .assert_package_cache_locked(CacheLockMode::DownloadExclusive, &path);
let unpack_dir = path.parent().unwrap();
match fs::read_to_string(path) {
Ok(ok) => match serde_json::from_str::<LockMetadata>(&ok) {
@@ -709,26 +712,33 @@ impl<'cfg> Source for RegistrySource<'cfg> {
kind: QueryKind,
f: &mut dyn FnMut(Summary),
) -> Poll<CargoResult<()>> {
- // If this is a precise dependency, then it came from a lock file and in
+ let mut req = dep.version_req().clone();
+
+ // Handle `cargo update --precise` here.
+ if let Some((_, requested)) = self
+ .source_id
+ .precise_registry_version(dep.package_name().as_str())
+ .filter(|(c, _)| req.matches(c))
+ {
+ req.update_precise(&requested);
+ }
+
+ // If this is a locked dependency, then it came from a lock file and in
// theory the registry is known to contain this version. If, however, we
// come back with no summaries, then our registry may need to be
// updated, so we fall back to performing a lazy update.
- if kind == QueryKind::Exact && dep.source_id().precise().is_some() && !self.ops.is_updated()
- {
+ if kind == QueryKind::Exact && req.is_locked() && !self.ops.is_updated() {
debug!("attempting query without update");
let mut called = false;
- ready!(self.index.query_inner(
- dep.package_name(),
- dep.version_req(),
- &mut *self.ops,
- &self.yanked_whitelist,
- &mut |s| {
- if dep.matches(&s) {
+ ready!(self
+ .index
+ .query_inner(dep.package_name(), &req, &mut *self.ops, &mut |s| {
+ if dep.matches(s.as_summary()) {
+ // We are looking for a package from a lock file so we do not care about yank
called = true;
- f(s);
+ f(s.into_summary());
}
- },
- ))?;
+ },))?;
if called {
Poll::Ready(Ok(()))
} else {
@@ -738,22 +748,23 @@ impl<'cfg> Source for RegistrySource<'cfg> {
}
} else {
let mut called = false;
- ready!(self.index.query_inner(
- dep.package_name(),
- dep.version_req(),
- &mut *self.ops,
- &self.yanked_whitelist,
- &mut |s| {
+ ready!(self
+ .index
+ .query_inner(dep.package_name(), &req, &mut *self.ops, &mut |s| {
let matched = match kind {
- QueryKind::Exact => dep.matches(&s),
+ QueryKind::Exact => dep.matches(s.as_summary()),
QueryKind::Fuzzy => true,
};
- if matched {
- f(s);
+ // Next filter out all yanked packages. Some yanked packages may
+ // leak through if they're in a whitelist (aka if they were
+ // previously in `Cargo.lock`
+ if matched
+ && (!s.is_yanked() || self.yanked_whitelist.contains(&s.package_id()))
+ {
+ f(s.into_summary());
called = true;
}
- }
- ))?;
+ }))?;
if called {
return Poll::Ready(Ok(()));
}
@@ -775,13 +786,9 @@ impl<'cfg> Source for RegistrySource<'cfg> {
}
any_pending |= self
.index
- .query_inner(
- name_permutation,
- dep.version_req(),
- &mut *self.ops,
- &self.yanked_whitelist,
- f,
- )?
+ .query_inner(name_permutation, &req, &mut *self.ops, &mut |s| {
+ f(s.into_summary());
+ })?
.is_pending();
}
}
diff --git a/src/tools/cargo/src/cargo/sources/registry/remote.rs b/src/tools/cargo/src/cargo/sources/registry/remote.rs
index 39eb14dc0..ba171eac3 100644
--- a/src/tools/cargo/src/cargo/sources/registry/remote.rs
+++ b/src/tools/cargo/src/cargo/sources/registry/remote.rs
@@ -6,6 +6,7 @@ use crate::sources::git::fetch::RemoteKind;
use crate::sources::registry::download;
use crate::sources::registry::MaybeLock;
use crate::sources::registry::{LoadResponse, RegistryConfig, RegistryData};
+use crate::util::cache_lock::CacheLockMode;
use crate::util::errors::CargoResult;
use crate::util::interning::InternedString;
use crate::util::{Config, Filesystem};
@@ -104,7 +105,9 @@ impl<'cfg> RemoteRegistry<'cfg> {
fn repo(&self) -> CargoResult<&git2::Repository> {
self.repo.try_borrow_with(|| {
trace!("acquiring registry index lock");
- let path = self.config.assert_package_cache_locked(&self.index_path);
+ let path = self
+ .config
+ .assert_package_cache_locked(CacheLockMode::DownloadExclusive, &self.index_path);
match git2::Repository::open(&path) {
Ok(repo) => Ok(repo),
@@ -216,7 +219,8 @@ impl<'cfg> RegistryData for RemoteRegistry<'cfg> {
}
fn assert_index_locked<'a>(&self, path: &'a Filesystem) -> &'a Path {
- self.config.assert_package_cache_locked(path)
+ self.config
+ .assert_package_cache_locked(CacheLockMode::DownloadExclusive, path)
}
/// Read the general concept for `load()` on [`RegistryData::load`].
@@ -302,7 +306,8 @@ impl<'cfg> RegistryData for RemoteRegistry<'cfg> {
fn config(&mut self) -> Poll<CargoResult<Option<RegistryConfig>>> {
debug!("loading config");
self.prepare()?;
- self.config.assert_package_cache_locked(&self.index_path);
+ self.config
+ .assert_package_cache_locked(CacheLockMode::DownloadExclusive, &self.index_path);
match ready!(self.load(Path::new(""), Path::new(RegistryConfig::NAME), None)?) {
LoadResponse::Data { raw_data, .. } => {
trace!("config loaded");
@@ -346,7 +351,9 @@ impl<'cfg> RegistryData for RemoteRegistry<'cfg> {
self.head.set(None);
*self.tree.borrow_mut() = None;
self.current_sha.set(None);
- let _path = self.config.assert_package_cache_locked(&self.index_path);
+ let _path = self
+ .config
+ .assert_package_cache_locked(CacheLockMode::DownloadExclusive, &self.index_path);
if !self.quiet {
self.config
.shell()
diff --git a/src/tools/cargo/src/cargo/util/auth/mod.rs b/src/tools/cargo/src/cargo/util/auth/mod.rs
index ea82dce0c..c2f818645 100644
--- a/src/tools/cargo/src/cargo/util/auth/mod.rs
+++ b/src/tools/cargo/src/cargo/util/auth/mod.rs
@@ -529,9 +529,15 @@ fn credential_action(
}
"cargo:paseto" => bail!("cargo:paseto requires -Zasymmetric-token"),
"cargo:token-from-stdout" => Box::new(BasicProcessCredential {}),
+ #[cfg(windows)]
"cargo:wincred" => Box::new(cargo_credential_wincred::WindowsCredential {}),
+ #[cfg(target_os = "macos")]
"cargo:macos-keychain" => Box::new(cargo_credential_macos_keychain::MacKeychain {}),
+ #[cfg(target_os = "linux")]
"cargo:libsecret" => Box::new(cargo_credential_libsecret::LibSecretCredential {}),
+ name if BUILT_IN_PROVIDERS.contains(&name) => {
+ Box::new(cargo_credential::UnsupportedCredential {})
+ }
process => Box::new(CredentialProcessCredential::new(process)),
};
config.shell().verbose(|c| {
diff --git a/src/tools/cargo/src/cargo/util/cache_lock.rs b/src/tools/cargo/src/cargo/util/cache_lock.rs
new file mode 100644
index 000000000..ca9e8d1b0
--- /dev/null
+++ b/src/tools/cargo/src/cargo/util/cache_lock.rs
@@ -0,0 +1,549 @@
+//! Support for locking the package and index caches.
+//!
+//! This implements locking on the package and index caches (source files,
+//! `.crate` files, and index caches) to coordinate when multiple cargos are
+//! running at the same time.
+//!
+//! ## Usage
+//!
+//! There is a global [`CacheLocker`] held inside cargo's venerable
+//! [`Config`]. The `CacheLocker` manages creating and tracking the locks
+//! being held. There are methods on `Config` for managing the locks:
+//!
+//! - [`Config::acquire_package_cache_lock`] --- Acquires a lock. May block if
+//! another process holds a lock.
+//! - [`Config::try_acquire_package_cache_lock`] --- Acquires a lock, returning
+//! immediately if it would block.
+//! - [`Config::assert_package_cache_locked`] --- This is used to ensure the
+//! proper lock is being held.
+//!
+//! Lower-level code that accesses the package cache typically just use
+//! `assert_package_cache_locked` to ensure that the correct lock is being
+//! held. Higher-level code is responsible for acquiring the appropriate lock,
+//! and holding it during the duration that it is performing its operation.
+//!
+//! ## Types of locking
+//!
+//! There are three styles of locks:
+//!
+//! * [`CacheLockMode::DownloadExclusive`] -- This is an exclusive lock
+//! acquired while downloading packages and doing resolution.
+//! * [`CacheLockMode::Shared`] -- This is a shared lock acquired while a
+//! build is running. In other words, whenever cargo just needs to read from
+//! the cache, it should hold this lock. This is here to ensure that no
+//! cargos are trying to read the source caches when cache garbage
+//! collection runs.
+//! * [`CacheLockMode::MutateExclusive`] -- This is an exclusive lock acquired
+//! whenever needing to modify existing source files (for example, with
+//! cache garbage collection). This is acquired to make sure that no other
+//! cargo is reading from the cache.
+//!
+//! Importantly, a `DownloadExclusive` lock does *not* interfere with a
+//! `Shared` lock. The download process generally does not modify source files
+//! (it only adds new ones), so other cargos should be able to safely proceed
+//! in reading source files[^1].
+//!
+//! See the [`CacheLockMode`] enum docs for more details on when the different
+//! modes should be used.
+//!
+//! ## Locking implementation details
+//!
+//! This is implemented by two separate lock files, the "download" one and the
+//! "mutate" one. The `MutateExclusive` lock acquired both the "mutate" and
+//! "download" locks. The `Shared` lock acquires the "mutate" lock in share
+//! mode.
+//!
+//! An important rule is that `MutateExclusive` acquires the locks in the
+//! order "mutate" first and then the "download". That helps prevent
+//! deadlocks. It is not allowed for a cargo to first acquire a
+//! `DownloadExclusive` lock and then a `Shared` lock because that would open
+//! it up for deadlock.
+//!
+//! Another rule is that there should be only one [`CacheLocker`] per process
+//! to uphold the ordering rules. You could in theory have multiple if you
+//! could ensure that other threads would make progress and drop a lock, but
+//! cargo is not architected that way.
+//!
+//! It is safe to recursively acquire a lock as many times as you want.
+//!
+//! ## Interaction with older cargos
+//!
+//! Before version 1.74, cargo only acquired the `DownloadExclusive` lock when
+//! downloading and doing resolution. Newer cargos that acquire
+//! `MutateExclusive` should still correctly block when an old cargo is
+//! downloading (because it also acquires `DownloadExclusive`), but they do
+//! not properly coordinate when an old cargo is in the build phase (because
+//! it holds no locks). This isn't expected to be much of a problem because
+//! the intended use of mutating the cache is only to delete old contents
+//! which aren't currently being used. It is possible for there to be a
+//! conflict, particularly if the user manually deletes the entire cache, but
+//! it is not expected for this scenario to happen too often, and the only
+//! consequence is that one side or the other encounters an error and needs to
+//! retry.
+//!
+//! [^1]: A minor caveat is that downloads will delete an existing `src`
+//! directory if it was extracted via an old cargo. See
+//! [`crate::sources::registry::RegistrySource::unpack_package`]. This
+//! should probably be fixed, but is unlikely to be a problem if the user is
+//! only using versions of cargo with the same deletion logic.
+
+use super::FileLock;
+use crate::CargoResult;
+use crate::Config;
+use anyhow::Context;
+use std::cell::RefCell;
+use std::io;
+
+/// The style of lock to acquire.
+#[derive(Copy, Clone, Debug, PartialEq)]
+pub enum CacheLockMode {
+ /// A `DownloadExclusive` lock ensures that only one cargo is doing
+ /// resolution and downloading new packages.
+ ///
+ /// You should use this when downloading new packages or doing resolution.
+ ///
+ /// If another cargo has a `MutateExclusive` lock, then an attempt to get
+ /// a `DownloadExclusive` lock will block.
+ ///
+ /// If another cargo has a `Shared` lock, then both can operate
+ /// concurrently.
+ DownloadExclusive,
+ /// A `Shared` lock allows multiple cargos to read from the source files.
+ ///
+ /// You should use this when cargo is reading source files from the
+ /// package cache. This is typically done during the build phase, since
+ /// cargo only needs to read files during that time. This allows multiple
+ /// cargo processes to build concurrently without interfering with one
+ /// another, while guarding against other cargos using `MutateExclusive`.
+ ///
+ /// If another cargo has a `MutateExclusive` lock, then an attempt to get
+ /// a `Shared` will block.
+ ///
+ /// If another cargo has a `DownloadExclusive` lock, then they both can
+ /// operate concurrently under the assumption that downloading does not
+ /// modify existing source files.
+ Shared,
+ /// A `MutateExclusive` lock ensures no other cargo is reading or writing
+ /// from the package caches.
+ ///
+ /// You should use this when modifying existing files in the package
+ /// cache. For example, things like garbage collection want to avoid
+ /// deleting files while other cargos are trying to read (`Shared`) or
+ /// resolve or download (`DownloadExclusive`).
+ ///
+ /// If another cargo has a `DownloadExclusive` or `Shared` lock, then this
+ /// will block until they all release their locks.
+ MutateExclusive,
+}
+
+/// Whether or not a lock attempt should block.
+#[derive(Copy, Clone)]
+enum BlockingMode {
+ Blocking,
+ NonBlocking,
+}
+
+use BlockingMode::*;
+
+/// Whether or not a lock attempt blocked or succeeded.
+#[derive(PartialEq, Copy, Clone)]
+#[must_use]
+enum LockingResult {
+ LockAcquired,
+ WouldBlock,
+}
+
+use LockingResult::*;
+
+/// A file lock, with a counter to assist with recursive locking.
+#[derive(Debug)]
+struct RecursiveLock {
+ /// The file lock.
+ ///
+ /// An important note is that locks can be `None` even when they are held.
+ /// This can happen on things like old NFS mounts where locking isn't
+ /// supported. We otherwise pretend we have a lock via the lock count. See
+ /// [`FileLock`] for more detail on that.
+ lock: Option<FileLock>,
+ /// Number locks held, to support recursive locking.
+ count: u32,
+ /// If this is `true`, it is an exclusive lock, otherwise it is shared.
+ is_exclusive: bool,
+ /// The filename of the lock.
+ filename: &'static str,
+}
+
+impl RecursiveLock {
+ fn new(filename: &'static str) -> RecursiveLock {
+ RecursiveLock {
+ lock: None,
+ count: 0,
+ is_exclusive: false,
+ filename,
+ }
+ }
+
+ /// Low-level lock count increment routine.
+ fn increment(&mut self) {
+ self.count = self.count.checked_add(1).unwrap();
+ }
+
+ /// Unlocks a previously acquired lock.
+ fn decrement(&mut self) {
+ let new_cnt = self.count.checked_sub(1).unwrap();
+ self.count = new_cnt;
+ if new_cnt == 0 {
+ // This will drop, releasing the lock.
+ self.lock = None;
+ }
+ }
+
+ /// Acquires a shared lock.
+ fn lock_shared(
+ &mut self,
+ config: &Config,
+ description: &'static str,
+ blocking: BlockingMode,
+ ) -> LockingResult {
+ match blocking {
+ Blocking => {
+ self.lock_shared_blocking(config, description);
+ LockAcquired
+ }
+ NonBlocking => self.lock_shared_nonblocking(config),
+ }
+ }
+
+ /// Acquires a shared lock, blocking if held by another locker.
+ fn lock_shared_blocking(&mut self, config: &Config, description: &'static str) {
+ if self.count == 0 {
+ self.is_exclusive = false;
+ self.lock =
+ match config
+ .home()
+ .open_ro_shared_create(self.filename, config, description)
+ {
+ Ok(lock) => Some(lock),
+ Err(e) => {
+ // There is no error here because locking is mostly a
+ // best-effort attempt. If cargo home is read-only, we don't
+ // want to fail just because we couldn't create the lock file.
+ tracing::warn!("failed to acquire cache lock {}: {e:?}", self.filename);
+ None
+ }
+ };
+ }
+ self.increment();
+ }
+
+ /// Acquires a shared lock, returns [`WouldBlock`] if held by another locker.
+ fn lock_shared_nonblocking(&mut self, config: &Config) -> LockingResult {
+ if self.count == 0 {
+ self.is_exclusive = false;
+ self.lock = match config.home().try_open_ro_shared_create(self.filename) {
+ Ok(Some(lock)) => Some(lock),
+ Ok(None) => {
+ return WouldBlock;
+ }
+ Err(e) => {
+ // Pretend that the lock was acquired (see lock_shared_blocking).
+ tracing::warn!("failed to acquire cache lock {}: {e:?}", self.filename);
+ None
+ }
+ };
+ }
+ self.increment();
+ LockAcquired
+ }
+
+ /// Acquires an exclusive lock.
+ fn lock_exclusive(
+ &mut self,
+ config: &Config,
+ description: &'static str,
+ blocking: BlockingMode,
+ ) -> CargoResult<LockingResult> {
+ if self.count > 0 && !self.is_exclusive {
+ // Lock upgrades are dicey. It might be possible to support
+ // this but would take a bit of work, and so far it isn't
+ // needed.
+ panic!("lock upgrade from shared to exclusive not supported");
+ }
+ match blocking {
+ Blocking => {
+ self.lock_exclusive_blocking(config, description)?;
+ Ok(LockAcquired)
+ }
+ NonBlocking => self.lock_exclusive_nonblocking(config),
+ }
+ }
+
+ /// Acquires an exclusive lock, blocking if held by another locker.
+ fn lock_exclusive_blocking(
+ &mut self,
+ config: &Config,
+ description: &'static str,
+ ) -> CargoResult<()> {
+ if self.count == 0 {
+ self.is_exclusive = true;
+ match config
+ .home()
+ .open_rw_exclusive_create(self.filename, config, description)
+ {
+ Ok(lock) => self.lock = Some(lock),
+ Err(e) => {
+ if maybe_readonly(&e) {
+ // This is a best-effort attempt to at least try to
+ // acquire some sort of lock. This can help in the
+ // situation where this cargo only has read-only access,
+ // but maybe some other cargo has read-write. This will at
+ // least attempt to coordinate with it.
+ //
+ // We don't want to fail on a read-only mount because
+ // cargo grabs an exclusive lock in situations where it
+ // may only be reading from the package cache. In that
+ // case, cargo isn't writing anything, and we don't want
+ // to fail on that.
+ self.lock_shared_blocking(config, description);
+ // This has to pretend it is exclusive for recursive locks to work.
+ self.is_exclusive = true;
+ return Ok(());
+ } else {
+ return Err(e).with_context(|| "failed to acquire package cache lock");
+ }
+ }
+ }
+ }
+ self.increment();
+ Ok(())
+ }
+
+ /// Acquires an exclusive lock, returns [`WouldBlock`] if held by another locker.
+ fn lock_exclusive_nonblocking(&mut self, config: &Config) -> CargoResult<LockingResult> {
+ if self.count == 0 {
+ self.is_exclusive = true;
+ match config.home().try_open_rw_exclusive_create(self.filename) {
+ Ok(Some(lock)) => self.lock = Some(lock),
+ Ok(None) => return Ok(WouldBlock),
+ Err(e) => {
+ if maybe_readonly(&e) {
+ let result = self.lock_shared_nonblocking(config);
+ // This has to pretend it is exclusive for recursive locks to work.
+ self.is_exclusive = true;
+ return Ok(result);
+ } else {
+ return Err(e).with_context(|| "failed to acquire package cache lock");
+ }
+ }
+ }
+ }
+ self.increment();
+ Ok(LockAcquired)
+ }
+}
+
+/// The state of the [`CacheLocker`].
+#[derive(Debug)]
+struct CacheState {
+ /// The cache lock guards the package cache used for download and
+ /// resolution (append operations that should not interfere with reading
+ /// from existing src files).
+ cache_lock: RecursiveLock,
+ /// The mutate lock is used to either guard the entire package cache for
+ /// destructive modifications (in exclusive mode), or for reading the
+ /// package cache src files (in shared mode).
+ ///
+ /// Note that [`CacheLockMode::MutateExclusive`] holds both
+ /// [`CacheState::mutate_lock`] and [`CacheState::cache_lock`].
+ mutate_lock: RecursiveLock,
+}
+
+impl CacheState {
+ fn lock(
+ &mut self,
+ config: &Config,
+ mode: CacheLockMode,
+ blocking: BlockingMode,
+ ) -> CargoResult<LockingResult> {
+ use CacheLockMode::*;
+ if mode == Shared && self.cache_lock.count > 0 && self.mutate_lock.count == 0 {
+ // Shared lock, when a DownloadExclusive is held.
+ //
+ // This isn't supported because it could cause a deadlock. If
+ // one cargo is attempting to acquire a MutateExclusive lock,
+ // and acquires the mutate lock, but is blocked on the
+ // download lock, and the cargo that holds the download lock
+ // attempts to get a shared lock, they would end up blocking
+ // each other.
+ panic!("shared lock while holding download lock is not allowed");
+ }
+ match mode {
+ Shared => {
+ if self.mutate_lock.lock_shared(config, SHARED_DESCR, blocking) == WouldBlock {
+ return Ok(WouldBlock);
+ }
+ }
+ DownloadExclusive => {
+ if self
+ .cache_lock
+ .lock_exclusive(config, DOWNLOAD_EXCLUSIVE_DESCR, blocking)?
+ == WouldBlock
+ {
+ return Ok(WouldBlock);
+ }
+ }
+ MutateExclusive => {
+ if self
+ .mutate_lock
+ .lock_exclusive(config, MUTATE_EXCLUSIVE_DESCR, blocking)?
+ == WouldBlock
+ {
+ return Ok(WouldBlock);
+ }
+
+ // Part of the contract of MutateExclusive is that it doesn't
+ // allow any processes to have a lock on the package cache, so
+ // this acquires both locks.
+ match self
+ .cache_lock
+ .lock_exclusive(config, DOWNLOAD_EXCLUSIVE_DESCR, blocking)
+ {
+ Ok(LockAcquired) => {}
+ Ok(WouldBlock) => return Ok(WouldBlock),
+ Err(e) => {
+ self.mutate_lock.decrement();
+ return Err(e);
+ }
+ }
+ }
+ }
+ Ok(LockAcquired)
+ }
+}
+
+/// A held lock guard.
+///
+/// When this is dropped, the lock will be released.
+#[must_use]
+pub struct CacheLock<'lock> {
+ mode: CacheLockMode,
+ locker: &'lock CacheLocker,
+}
+
+impl Drop for CacheLock<'_> {
+ fn drop(&mut self) {
+ use CacheLockMode::*;
+ let mut state = self.locker.state.borrow_mut();
+ match self.mode {
+ Shared => {
+ state.mutate_lock.decrement();
+ }
+ DownloadExclusive => {
+ state.cache_lock.decrement();
+ }
+ MutateExclusive => {
+ state.cache_lock.decrement();
+ state.mutate_lock.decrement();
+ }
+ }
+ }
+}
+
+/// The filename for the [`CacheLockMode::DownloadExclusive`] lock.
+const CACHE_LOCK_NAME: &str = ".package-cache";
+/// The filename for the [`CacheLockMode::MutateExclusive`] and
+/// [`CacheLockMode::Shared`] lock.
+const MUTATE_NAME: &str = ".package-cache-mutate";
+
+// Descriptions that are displayed in the "Blocking" message shown to the user.
+const SHARED_DESCR: &str = "shared package cache";
+const DOWNLOAD_EXCLUSIVE_DESCR: &str = "package cache";
+const MUTATE_EXCLUSIVE_DESCR: &str = "package cache mutation";
+
+/// A locker that can be used to acquire locks.
+///
+/// See the [`crate::util::cache_lock`] module documentation for an overview
+/// of how cache locking works.
+#[derive(Debug)]
+pub struct CacheLocker {
+ /// The state of the locker.
+ ///
+ /// [`CacheLocker`] uses interior mutability because it is stuffed inside
+ /// the global `Config`, which does not allow mutation.
+ state: RefCell<CacheState>,
+}
+
+impl CacheLocker {
+ /// Creates a new `CacheLocker`.
+ pub fn new() -> CacheLocker {
+ CacheLocker {
+ state: RefCell::new(CacheState {
+ cache_lock: RecursiveLock::new(CACHE_LOCK_NAME),
+ mutate_lock: RecursiveLock::new(MUTATE_NAME),
+ }),
+ }
+ }
+
+ /// Acquires a lock with the given mode, possibly blocking if another
+ /// cargo is holding the lock.
+ pub fn lock(&self, config: &Config, mode: CacheLockMode) -> CargoResult<CacheLock<'_>> {
+ let mut state = self.state.borrow_mut();
+ let _ = state.lock(config, mode, Blocking)?;
+ Ok(CacheLock { mode, locker: self })
+ }
+
+ /// Acquires a lock with the given mode, returning `None` if another cargo
+ /// is holding the lock.
+ pub fn try_lock(
+ &self,
+ config: &Config,
+ mode: CacheLockMode,
+ ) -> CargoResult<Option<CacheLock<'_>>> {
+ let mut state = self.state.borrow_mut();
+ if state.lock(config, mode, NonBlocking)? == LockAcquired {
+ Ok(Some(CacheLock { mode, locker: self }))
+ } else {
+ Ok(None)
+ }
+ }
+
+ /// Returns whether or not a lock is held for the given mode in this locker.
+ ///
+ /// This does not tell you whether or not it is locked in some other
+ /// locker (such as in another process).
+ ///
+ /// Note that `Shared` will return true if a `MutateExclusive` lock is
+ /// held, since `MutateExclusive` is just an upgraded `Shared`. Likewise,
+ /// `DownlaodExclusive` will return true if a `MutateExclusive` lock is
+ /// held since they overlap.
+ pub fn is_locked(&self, mode: CacheLockMode) -> bool {
+ let state = self.state.borrow();
+ match (
+ mode,
+ state.cache_lock.count,
+ state.mutate_lock.count,
+ state.mutate_lock.is_exclusive,
+ ) {
+ (CacheLockMode::Shared, _, 1.., _) => true,
+ (CacheLockMode::MutateExclusive, _, 1.., true) => true,
+ (CacheLockMode::DownloadExclusive, 1.., _, _) => true,
+ _ => false,
+ }
+ }
+}
+
+/// Returns whether or not the error appears to be from a read-only filesystem.
+fn maybe_readonly(err: &anyhow::Error) -> bool {
+ err.chain().any(|err| {
+ if let Some(io) = err.downcast_ref::<io::Error>() {
+ if io.kind() == io::ErrorKind::PermissionDenied {
+ return true;
+ }
+
+ #[cfg(unix)]
+ return io.raw_os_error() == Some(libc::EROFS);
+ }
+
+ false
+ })
+}
diff --git a/src/tools/cargo/src/cargo/util/command_prelude.rs b/src/tools/cargo/src/cargo/util/command_prelude.rs
index bd8889bef..3888b80c4 100644
--- a/src/tools/cargo/src/cargo/util/command_prelude.rs
+++ b/src/tools/cargo/src/cargo/util/command_prelude.rs
@@ -6,9 +6,8 @@ use crate::ops::{CompileFilter, CompileOptions, NewOptions, Packages, VersionCon
use crate::util::important_paths::find_root_manifest_for_wd;
use crate::util::interning::InternedString;
use crate::util::is_rustup;
-use crate::util::restricted_names::is_glob_pattern;
-use crate::util::toml::{StringOrVec, TomlProfile};
-use crate::util::validate_package_name;
+use crate::util::restricted_names;
+use crate::util::toml::schema::StringOrVec;
use crate::util::{
print_available_benches, print_available_binaries, print_available_examples,
print_available_packages, print_available_tests,
@@ -64,9 +63,19 @@ pub trait CommandExt: Sized {
all: &'static str,
exclude: &'static str,
) -> Self {
+ let unsupported_short_arg = {
+ let value_parser = UnknownArgumentValueParser::suggest_arg("--exclude");
+ Arg::new("unsupported-short-exclude-flag")
+ .help("")
+ .short('x')
+ .value_parser(value_parser)
+ .action(ArgAction::SetTrue)
+ .hide(true)
+ };
self.arg_package_spec_simple(package)
._arg(flag("workspace", all).help_heading(heading::PACKAGE_SELECTION))
._arg(multi_opt("exclude", "SPEC", exclude).help_heading(heading::PACKAGE_SELECTION))
+ ._arg(unsupported_short_arg)
}
fn arg_package_spec_simple(self, package: &'static str) -> Self {
@@ -232,10 +241,20 @@ pub trait CommandExt: Sized {
}
fn arg_target_triple(self, target: &'static str) -> Self {
+ let unsupported_short_arg = {
+ let value_parser = UnknownArgumentValueParser::suggest_arg("--target");
+ Arg::new("unsupported-short-target-flag")
+ .help("")
+ .short('t')
+ .value_parser(value_parser)
+ .action(ArgAction::SetTrue)
+ .hide(true)
+ };
self._arg(
optional_multi_opt("target", "TRIPLE", target)
.help_heading(heading::COMPILATION_OPTIONS),
)
+ ._arg(unsupported_short_arg)
}
fn arg_target_dir(self) -> Self {
@@ -247,6 +266,20 @@ pub trait CommandExt: Sized {
}
fn arg_manifest_path(self) -> Self {
+ // We use `--manifest-path` instead of `--path`.
+ let unsupported_path_arg = {
+ let value_parser = UnknownArgumentValueParser::suggest_arg("--manifest-path");
+ flag("unsupported-path-flag", "")
+ .long("path")
+ .value_parser(value_parser)
+ .hide(true)
+ };
+ self.arg_manifest_path_without_unsupported_path_tip()
+ ._arg(unsupported_path_arg)
+ }
+
+ // `cargo add` has a `--path` flag to install a crate from a local path.
+ fn arg_manifest_path_without_unsupported_path_tip(self) -> Self {
self._arg(
opt("manifest-path", "Path to Cargo.toml")
.value_name("PATH")
@@ -338,7 +371,7 @@ pub trait CommandExt: Sized {
.value_parser(value_parser)
.hide(true)
};
- self._arg(flag("quiet", "Do not print cargo log messages").short('q'))
+ self.arg_quiet_without_unknown_silent_arg_tip()
._arg(unsupported_silent_arg)
}
@@ -357,6 +390,27 @@ pub trait CommandExt: Sized {
.help_heading(heading::COMPILATION_OPTIONS),
)
}
+
+ fn arg_out_dir(self) -> Self {
+ let unsupported_short_arg = {
+ let value_parser = UnknownArgumentValueParser::suggest_arg("--out-dir");
+ Arg::new("unsupported-short-out-dir-flag")
+ .help("")
+ .short('O')
+ .value_parser(value_parser)
+ .action(ArgAction::SetTrue)
+ .hide(true)
+ };
+ self._arg(
+ opt(
+ "out-dir",
+ "Copy final artifacts to this directory (unstable)",
+ )
+ .value_name("PATH")
+ .help_heading(heading::COMPILATION_OPTIONS),
+ )
+ ._arg(unsupported_short_arg)
+ }
}
impl CommandExt for Command {
@@ -552,7 +606,7 @@ Run `{cmd}` to see possible targets."
bail!("profile `doc` is reserved and not allowed to be explicitly specified")
}
(_, _, Some(name)) => {
- TomlProfile::validate_name(name)?;
+ restricted_names::validate_profile_name(name)?;
name
}
};
@@ -746,7 +800,7 @@ Run `{cmd}` to see possible targets."
) -> CargoResult<CompileOptions> {
let mut compile_opts = self.compile_options(config, mode, workspace, profile_checking)?;
let spec = self._values_of("package");
- if spec.iter().any(is_glob_pattern) {
+ if spec.iter().any(restricted_names::is_glob_pattern) {
anyhow::bail!("Glob patterns on package selection are not supported.")
}
compile_opts.spec = Packages::Packages(spec);
@@ -780,7 +834,7 @@ Run `{cmd}` to see possible targets."
(None, None) => config.default_registry()?.map(RegistryOrIndex::Registry),
(None, Some(i)) => Some(RegistryOrIndex::Index(i.into_url()?)),
(Some(r), None) => {
- validate_package_name(r, "registry name", "")?;
+ restricted_names::validate_package_name(r, "registry name", "")?;
Some(RegistryOrIndex::Registry(r.to_string()))
}
(Some(_), Some(_)) => {
@@ -795,7 +849,7 @@ Run `{cmd}` to see possible targets."
match self._value_of("registry").map(|s| s.to_string()) {
None => config.default_registry(),
Some(registry) => {
- validate_package_name(&registry, "registry name", "")?;
+ restricted_names::validate_package_name(&registry, "registry name", "")?;
Ok(Some(registry))
}
}
diff --git a/src/tools/cargo/src/cargo/util/config/mod.rs b/src/tools/cargo/src/cargo/util/config/mod.rs
index b87f98afd..50153466b 100644
--- a/src/tools/cargo/src/cargo/util/config/mod.rs
+++ b/src/tools/cargo/src/cargo/util/config/mod.rs
@@ -49,6 +49,7 @@
//! translate from `ConfigValue` and environment variables to the caller's
//! desired type.
+use crate::util::cache_lock::{CacheLock, CacheLockMode, CacheLocker};
use std::borrow::Cow;
use std::cell::{RefCell, RefMut};
use std::collections::hash_map::Entry::{Occupied, Vacant};
@@ -58,7 +59,7 @@ use std::ffi::{OsStr, OsString};
use std::fmt;
use std::fs::{self, File};
use std::io::prelude::*;
-use std::io::{self, SeekFrom};
+use std::io::SeekFrom;
use std::mem;
use std::path::{Path, PathBuf};
use std::str::FromStr;
@@ -75,10 +76,9 @@ use crate::sources::CRATES_IO_REGISTRY;
use crate::util::errors::CargoResult;
use crate::util::network::http::configure_http_handle;
use crate::util::network::http::http_handle;
-use crate::util::toml as cargo_toml;
use crate::util::{internal, CanonicalUrl};
use crate::util::{try_canonicalize, validate_package_name};
-use crate::util::{FileLock, Filesystem, IntoUrl, IntoUrlWithBase, Rustc};
+use crate::util::{Filesystem, IntoUrl, IntoUrlWithBase, Rustc};
use anyhow::{anyhow, bail, format_err, Context as _};
use cargo_credential::Secret;
use cargo_util::paths;
@@ -215,9 +215,8 @@ pub struct Config {
credential_cache: LazyCell<RefCell<HashMap<CanonicalUrl, CredentialCacheValue>>>,
/// Cache of registry config from from the `[registries]` table.
registry_config: LazyCell<RefCell<HashMap<SourceId, Option<RegistryConfig>>>>,
- /// Lock, if held, of the global package cache along with the number of
- /// acquisitions so far.
- package_cache_lock: RefCell<Option<(Option<FileLock>, usize)>>,
+ /// Locks on the package and index caches.
+ package_cache_lock: CacheLocker,
/// Cached configuration parsed by Cargo
http_config: LazyCell<CargoHttpConfig>,
future_incompat_config: LazyCell<CargoFutureIncompatConfig>,
@@ -307,7 +306,7 @@ impl Config {
updated_sources: LazyCell::new(),
credential_cache: LazyCell::new(),
registry_config: LazyCell::new(),
- package_cache_lock: RefCell::new(None),
+ package_cache_lock: CacheLocker::new(),
http_config: LazyCell::new(),
future_incompat_config: LazyCell::new(),
net_config: LazyCell::new(),
@@ -1032,6 +1031,9 @@ impl Config {
self.shell().set_verbosity(verbosity);
self.shell().set_color_choice(color)?;
+ if let Some(hyperlinks) = term.hyperlinks {
+ self.shell().set_hyperlinks(hyperlinks)?;
+ }
self.progress_config = term.progress.unwrap_or_default();
self.extra_verbose = extra_verbose;
self.frozen = frozen;
@@ -1195,7 +1197,7 @@ impl Config {
}
let contents = fs::read_to_string(path)
.with_context(|| format!("failed to read configuration file `{}`", path.display()))?;
- let toml = cargo_toml::parse_document(&contents, path, self).with_context(|| {
+ let toml = parse_document(&contents, path, self).with_context(|| {
format!("could not parse TOML configuration in `{}`", path.display())
})?;
let def = match why_load {
@@ -1876,10 +1878,20 @@ impl Config {
T::deserialize(d).map_err(|e| e.into())
}
- pub fn assert_package_cache_locked<'a>(&self, f: &'a Filesystem) -> &'a Path {
+ /// Obtain a [`Path`] from a [`Filesystem`], verifying that the
+ /// appropriate lock is already currently held.
+ ///
+ /// Locks are usually acquired via [`Config::acquire_package_cache_lock`]
+ /// or [`Config::try_acquire_package_cache_lock`].
+ #[track_caller]
+ pub fn assert_package_cache_locked<'a>(
+ &self,
+ mode: CacheLockMode,
+ f: &'a Filesystem,
+ ) -> &'a Path {
let ret = f.as_path_unlocked();
assert!(
- self.package_cache_lock.borrow().is_some(),
+ self.package_cache_lock.is_locked(mode),
"package cache lock is not currently held, Cargo forgot to call \
`acquire_package_cache_lock` before we got to this stack frame",
);
@@ -1887,72 +1899,26 @@ impl Config {
ret
}
- /// Acquires an exclusive lock on the global "package cache"
+ /// Acquires a lock on the global "package cache", blocking if another
+ /// cargo holds the lock.
///
- /// This lock is global per-process and can be acquired recursively. An RAII
- /// structure is returned to release the lock, and if this process
- /// abnormally terminates the lock is also released.
- pub fn acquire_package_cache_lock(&self) -> CargoResult<PackageCacheLock<'_>> {
- let mut slot = self.package_cache_lock.borrow_mut();
- match *slot {
- // We've already acquired the lock in this process, so simply bump
- // the count and continue.
- Some((_, ref mut cnt)) => {
- *cnt += 1;
- }
- None => {
- let path = ".package-cache";
- let desc = "package cache";
-
- // First, attempt to open an exclusive lock which is in general
- // the purpose of this lock!
- //
- // If that fails because of a readonly filesystem or a
- // permission error, though, then we don't really want to fail
- // just because of this. All files that this lock protects are
- // in subfolders, so they're assumed by Cargo to also be
- // readonly or have invalid permissions for us to write to. If
- // that's the case, then we don't really need to grab a lock in
- // the first place here.
- //
- // Despite this we attempt to grab a readonly lock. This means
- // that if our read-only folder is shared read-write with
- // someone else on the system we should synchronize with them,
- // but if we can't even do that then we did our best and we just
- // keep on chugging elsewhere.
- match self.home_path.open_rw(path, self, desc) {
- Ok(lock) => *slot = Some((Some(lock), 1)),
- Err(e) => {
- if maybe_readonly(&e) {
- let lock = self.home_path.open_ro(path, self, desc).ok();
- *slot = Some((lock, 1));
- return Ok(PackageCacheLock(self));
- }
-
- Err(e).with_context(|| "failed to acquire package cache lock")?;
- }
- }
- }
- }
- return Ok(PackageCacheLock(self));
-
- fn maybe_readonly(err: &anyhow::Error) -> bool {
- err.chain().any(|err| {
- if let Some(io) = err.downcast_ref::<io::Error>() {
- if io.kind() == io::ErrorKind::PermissionDenied {
- return true;
- }
-
- #[cfg(unix)]
- return io.raw_os_error() == Some(libc::EROFS);
- }
-
- false
- })
- }
+ /// See [`crate::util::cache_lock`] for an in-depth discussion of locking
+ /// and lock modes.
+ pub fn acquire_package_cache_lock(&self, mode: CacheLockMode) -> CargoResult<CacheLock<'_>> {
+ self.package_cache_lock.lock(self, mode)
}
- pub fn release_package_cache_lock(&self) {}
+ /// Acquires a lock on the global "package cache", returning `None` if
+ /// another cargo holds the lock.
+ ///
+ /// See [`crate::util::cache_lock`] for an in-depth discussion of locking
+ /// and lock modes.
+ pub fn try_acquire_package_cache_lock(
+ &self,
+ mode: CacheLockMode,
+ ) -> CargoResult<Option<CacheLock<'_>>> {
+ self.package_cache_lock.try_lock(self, mode)
+ }
}
/// Internal error for serde errors.
@@ -2271,7 +2237,7 @@ pub fn save_credentials(
let mut file = {
cfg.home_path.create_dir()?;
cfg.home_path
- .open_rw(filename, cfg, "credentials' config file")?
+ .open_rw_exclusive_create(filename, cfg, "credentials' config file")?
};
let mut contents = String::new();
@@ -2282,7 +2248,7 @@ pub fn save_credentials(
)
})?;
- let mut toml = cargo_toml::parse_document(&contents, file.path(), cfg)?;
+ let mut toml = parse_document(&contents, file.path(), cfg)?;
// Move the old token location to the new one.
if let Some(token) = toml.remove("token") {
@@ -2390,19 +2356,6 @@ pub fn save_credentials(
}
}
-pub struct PackageCacheLock<'a>(&'a Config);
-
-impl Drop for PackageCacheLock<'_> {
- fn drop(&mut self) {
- let mut slot = self.0.package_cache_lock.borrow_mut();
- let (_, cnt) = slot.as_mut().unwrap();
- *cnt -= 1;
- if *cnt == 0 {
- *slot = None;
- }
- }
-}
-
#[derive(Debug, Default, Deserialize, PartialEq)]
#[serde(rename_all = "kebab-case")]
pub struct CargoHttpConfig {
@@ -2609,6 +2562,7 @@ struct TermConfig {
verbose: Option<bool>,
quiet: Option<bool>,
color: Option<String>,
+ hyperlinks: Option<bool>,
#[serde(default)]
#[serde(deserialize_with = "progress_or_string")]
progress: Option<ProgressConfig>,
@@ -2761,6 +2715,11 @@ impl EnvConfigValue {
pub type EnvConfig = HashMap<String, EnvConfigValue>;
+fn parse_document(toml: &str, _file: &Path, _config: &Config) -> CargoResult<toml::Table> {
+ // At the moment, no compatibility checks are needed.
+ toml.parse().map_err(Into::into)
+}
+
/// A type to deserialize a list of strings from a toml file.
///
/// Supports deserializing either a whitespace-separated list of arguments in a
diff --git a/src/tools/cargo/src/cargo/util/config/target.rs b/src/tools/cargo/src/cargo/util/config/target.rs
index 6d6a6beff..0e2029ffc 100644
--- a/src/tools/cargo/src/cargo/util/config/target.rs
+++ b/src/tools/cargo/src/cargo/util/config/target.rs
@@ -137,10 +137,6 @@ fn parse_links_overrides(
config: &Config,
) -> CargoResult<BTreeMap<String, BuildOutput>> {
let mut links_overrides = BTreeMap::new();
- let extra_check_cfg = match config.cli_unstable().check_cfg {
- Some((_, _, _, output)) => output,
- None => false,
- };
for (lib_name, value) in links {
// Skip these keys, it shares the namespace with `TargetConfig`.
@@ -207,12 +203,12 @@ fn parse_links_overrides(
output.cfgs.extend(list.iter().map(|v| v.0.clone()));
}
"rustc-check-cfg" => {
- if extra_check_cfg {
+ if config.cli_unstable().check_cfg {
let list = value.list(key)?;
output.check_cfgs.extend(list.iter().map(|v| v.0.clone()));
} else {
config.shell().warn(format!(
- "target config `{}.{}` requires -Zcheck-cfg=output flag",
+ "target config `{}.{}` requires -Zcheck-cfg flag",
target_key, key
))?;
}
diff --git a/src/tools/cargo/src/cargo/util/flock.rs b/src/tools/cargo/src/cargo/util/flock.rs
index aa056c965..3fb2397f7 100644
--- a/src/tools/cargo/src/cargo/util/flock.rs
+++ b/src/tools/cargo/src/cargo/util/flock.rs
@@ -1,3 +1,12 @@
+//! File-locking support.
+//!
+//! This module defines the [`Filesystem`] type which is an abstraction over a
+//! filesystem, ensuring that access to the filesystem is only done through
+//! coordinated locks.
+//!
+//! The [`FileLock`] type represents a locked file, and provides access to the
+//! file.
+
use std::fs::{File, OpenOptions};
use std::io;
use std::io::{Read, Seek, SeekFrom, Write};
@@ -10,18 +19,22 @@ use anyhow::Context as _;
use cargo_util::paths;
use sys::*;
+/// A locked file.
+///
+/// This provides access to file while holding a lock on the file. This type
+/// implements the [`Read`], [`Write`], and [`Seek`] traits to provide access
+/// to the underlying file.
+///
+/// Locks are either shared (multiple processes can access the file) or
+/// exclusive (only one process can access the file).
+///
+/// This type is created via methods on the [`Filesystem`] type.
+///
+/// When this value is dropped, the lock will be released.
#[derive(Debug)]
pub struct FileLock {
f: Option<File>,
path: PathBuf,
- state: State,
-}
-
-#[derive(PartialEq, Debug)]
-enum State {
- Unlocked,
- Shared,
- Exclusive,
}
impl FileLock {
@@ -35,13 +48,11 @@ impl FileLock {
/// Note that special care must be taken to ensure that the path is not
/// referenced outside the lifetime of this lock.
pub fn path(&self) -> &Path {
- assert_ne!(self.state, State::Unlocked);
&self.path
}
/// Returns the parent path containing this file
pub fn parent(&self) -> &Path {
- assert_ne!(self.state, State::Unlocked);
self.path.parent().unwrap()
}
@@ -91,9 +102,9 @@ impl Write for FileLock {
impl Drop for FileLock {
fn drop(&mut self) {
- if self.state != State::Unlocked {
- if let Some(f) = self.f.take() {
- let _ = unlock(&f);
+ if let Some(f) = self.f.take() {
+ if let Err(e) = unlock(&f) {
+ tracing::warn!("failed to release lock: {e:?}");
}
}
}
@@ -105,6 +116,32 @@ impl Drop for FileLock {
/// The `Path` of a filesystem cannot be learned unless it's done in a locked
/// fashion, and otherwise functions on this structure are prepared to handle
/// concurrent invocations across multiple instances of Cargo.
+///
+/// The methods on `Filesystem` that open files return a [`FileLock`] which
+/// holds the lock, and that type provides methods for accessing the
+/// underlying file.
+///
+/// If the blocking methods (like [`Filesystem::open_ro_shared`]) detect that
+/// they will block, then they will display a message to the user letting them
+/// know it is blocked. There are non-blocking variants starting with the
+/// `try_` prefix like [`Filesystem::try_open_ro_shared_create`].
+///
+/// The behavior of locks acquired by the `Filesystem` depend on the operating
+/// system. On unix-like system, they are advisory using [`flock`], and thus
+/// not enforced against processes which do not try to acquire the lock. On
+/// Windows, they are mandatory using [`LockFileEx`], enforced against all
+/// processes.
+///
+/// This **does not** guarantee that a lock is acquired. In some cases, for
+/// example on filesystems that don't support locking, it will return a
+/// [`FileLock`] even though the filesystem lock was not acquired. This is
+/// intended to provide a graceful fallback instead of refusing to work.
+/// Usually there aren't multiple processes accessing the same resource. In
+/// that case, it is the user's responsibility to not run concurrent
+/// processes.
+///
+/// [`flock`]: https://linux.die.net/man/2/flock
+/// [`LockFileEx`]: https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-lockfileex
#[derive(Clone, Debug)]
pub struct Filesystem {
root: PathBuf,
@@ -157,68 +194,119 @@ impl Filesystem {
self.root.display()
}
- /// Opens exclusive access to a file, returning the locked version of a
- /// file.
+ /// Opens read-write exclusive access to a file, returning the locked
+ /// version of a file.
///
/// This function will create a file at `path` if it doesn't already exist
/// (including intermediate directories), and then it will acquire an
/// exclusive lock on `path`. If the process must block waiting for the
- /// lock, the `msg` is printed to `config`.
+ /// lock, the `msg` is printed to [`Config`].
///
/// The returned file can be accessed to look at the path and also has
/// read/write access to the underlying file.
- pub fn open_rw<P>(&self, path: P, config: &Config, msg: &str) -> CargoResult<FileLock>
+ pub fn open_rw_exclusive_create<P>(
+ &self,
+ path: P,
+ config: &Config,
+ msg: &str,
+ ) -> CargoResult<FileLock>
where
P: AsRef<Path>,
{
- self.open(
- path.as_ref(),
- OpenOptions::new().read(true).write(true).create(true),
- State::Exclusive,
- config,
- msg,
- )
+ let mut opts = OpenOptions::new();
+ opts.read(true).write(true).create(true);
+ let (path, f) = self.open(path.as_ref(), &opts, true)?;
+ acquire(config, msg, &path, &|| try_lock_exclusive(&f), &|| {
+ lock_exclusive(&f)
+ })?;
+ Ok(FileLock { f: Some(f), path })
}
- /// Opens shared access to a file, returning the locked version of a file.
+ /// A non-blocking version of [`Filesystem::open_rw_exclusive_create`].
+ ///
+ /// Returns `None` if the operation would block due to another process
+ /// holding the lock.
+ pub fn try_open_rw_exclusive_create<P: AsRef<Path>>(
+ &self,
+ path: P,
+ ) -> CargoResult<Option<FileLock>> {
+ let mut opts = OpenOptions::new();
+ opts.read(true).write(true).create(true);
+ let (path, f) = self.open(path.as_ref(), &opts, true)?;
+ if try_acquire(&path, &|| try_lock_exclusive(&f))? {
+ Ok(Some(FileLock { f: Some(f), path }))
+ } else {
+ Ok(None)
+ }
+ }
+
+ /// Opens read-only shared access to a file, returning the locked version of a file.
///
/// This function will fail if `path` doesn't already exist, but if it does
/// then it will acquire a shared lock on `path`. If the process must block
- /// waiting for the lock, the `msg` is printed to `config`.
+ /// waiting for the lock, the `msg` is printed to [`Config`].
///
/// The returned file can be accessed to look at the path and also has read
/// access to the underlying file. Any writes to the file will return an
/// error.
- pub fn open_ro<P>(&self, path: P, config: &Config, msg: &str) -> CargoResult<FileLock>
+ pub fn open_ro_shared<P>(&self, path: P, config: &Config, msg: &str) -> CargoResult<FileLock>
where
P: AsRef<Path>,
{
- self.open(
- path.as_ref(),
- OpenOptions::new().read(true),
- State::Shared,
- config,
- msg,
- )
+ let (path, f) = self.open(path.as_ref(), &OpenOptions::new().read(true), false)?;
+ acquire(config, msg, &path, &|| try_lock_shared(&f), &|| {
+ lock_shared(&f)
+ })?;
+ Ok(FileLock { f: Some(f), path })
}
- fn open(
+ /// Opens read-only shared access to a file, returning the locked version of a file.
+ ///
+ /// Compared to [`Filesystem::open_ro_shared`], this will create the file
+ /// (and any directories in the parent) if the file does not already
+ /// exist.
+ pub fn open_ro_shared_create<P: AsRef<Path>>(
&self,
- path: &Path,
- opts: &OpenOptions,
- state: State,
+ path: P,
config: &Config,
msg: &str,
) -> CargoResult<FileLock> {
- let path = self.root.join(path);
+ let mut opts = OpenOptions::new();
+ opts.read(true).write(true).create(true);
+ let (path, f) = self.open(path.as_ref(), &opts, true)?;
+ acquire(config, msg, &path, &|| try_lock_shared(&f), &|| {
+ lock_shared(&f)
+ })?;
+ Ok(FileLock { f: Some(f), path })
+ }
- // If we want an exclusive lock then if we fail because of NotFound it's
- // likely because an intermediate directory didn't exist, so try to
- // create the directory and then continue.
+ /// A non-blocking version of [`Filesystem::open_ro_shared_create`].
+ ///
+ /// Returns `None` if the operation would block due to another process
+ /// holding the lock.
+ pub fn try_open_ro_shared_create<P: AsRef<Path>>(
+ &self,
+ path: P,
+ ) -> CargoResult<Option<FileLock>> {
+ let mut opts = OpenOptions::new();
+ opts.read(true).write(true).create(true);
+ let (path, f) = self.open(path.as_ref(), &opts, true)?;
+ if try_acquire(&path, &|| try_lock_shared(&f))? {
+ Ok(Some(FileLock { f: Some(f), path }))
+ } else {
+ Ok(None)
+ }
+ }
+
+ fn open(&self, path: &Path, opts: &OpenOptions, create: bool) -> CargoResult<(PathBuf, File)> {
+ let path = self.root.join(path);
let f = opts
.open(&path)
.or_else(|e| {
- if e.kind() == io::ErrorKind::NotFound && state == State::Exclusive {
+ // If we were requested to create this file, and there was a
+ // NotFound error, then that was likely due to missing
+ // intermediate directories. Try creating them and try again.
+ if e.kind() == io::ErrorKind::NotFound && create {
paths::create_dir_all(path.parent().unwrap())?;
Ok(opts.open(&path)?)
} else {
@@ -226,24 +314,7 @@ impl Filesystem {
}
})
.with_context(|| format!("failed to open: {}", path.display()))?;
- match state {
- State::Exclusive => {
- acquire(config, msg, &path, &|| try_lock_exclusive(&f), &|| {
- lock_exclusive(&f)
- })?;
- }
- State::Shared => {
- acquire(config, msg, &path, &|| try_lock_shared(&f), &|| {
- lock_shared(&f)
- })?;
- }
- State::Unlocked => {}
- }
- Ok(FileLock {
- f: Some(f),
- path,
- state,
- })
+ Ok((path, f))
}
}
@@ -259,28 +330,7 @@ impl PartialEq<Filesystem> for Path {
}
}
-/// Acquires a lock on a file in a "nice" manner.
-///
-/// Almost all long-running blocking actions in Cargo have a status message
-/// associated with them as we're not sure how long they'll take. Whenever a
-/// conflicted file lock happens, this is the case (we're not sure when the lock
-/// will be released).
-///
-/// This function will acquire the lock on a `path`, printing out a nice message
-/// to the console if we have to wait for it. It will first attempt to use `try`
-/// to acquire a lock on the crate, and in the case of contention it will emit a
-/// status message based on `msg` to `config`'s shell, and then use `block` to
-/// block waiting to acquire a lock.
-///
-/// Returns an error if the lock could not be acquired or if any error other
-/// than a contention error happens.
-fn acquire(
- config: &Config,
- msg: &str,
- path: &Path,
- lock_try: &dyn Fn() -> io::Result<()>,
- lock_block: &dyn Fn() -> io::Result<()>,
-) -> CargoResult<()> {
+fn try_acquire(path: &Path, lock_try: &dyn Fn() -> io::Result<()>) -> CargoResult<bool> {
// File locking on Unix is currently implemented via `flock`, which is known
// to be broken on NFS. We could in theory just ignore errors that happen on
// NFS, but apparently the failure mode [1] for `flock` on NFS is **blocking
@@ -292,16 +342,17 @@ fn acquire(
//
// [1]: https://github.com/rust-lang/cargo/issues/2615
if is_on_nfs_mount(path) {
- return Ok(());
+ tracing::debug!("{path:?} appears to be an NFS mount, not trying to lock");
+ return Ok(true);
}
match lock_try() {
- Ok(()) => return Ok(()),
+ Ok(()) => return Ok(true),
// In addition to ignoring NFS which is commonly not working we also
// just ignore locking on filesystems that look like they don't
// implement file locking.
- Err(e) if error_unsupported(&e) => return Ok(()),
+ Err(e) if error_unsupported(&e) => return Ok(true),
Err(e) => {
if !error_contended(&e) {
@@ -311,36 +362,64 @@ fn acquire(
}
}
}
+ Ok(false)
+}
+
+/// Acquires a lock on a file in a "nice" manner.
+///
+/// Almost all long-running blocking actions in Cargo have a status message
+/// associated with them as we're not sure how long they'll take. Whenever a
+/// conflicted file lock happens, this is the case (we're not sure when the lock
+/// will be released).
+///
+/// This function will acquire the lock on a `path`, printing out a nice message
+/// to the console if we have to wait for it. It will first attempt to use `try`
+/// to acquire a lock on the crate, and in the case of contention it will emit a
+/// status message based on `msg` to [`Config`]'s shell, and then use `block` to
+/// block waiting to acquire a lock.
+///
+/// Returns an error if the lock could not be acquired or if any error other
+/// than a contention error happens.
+fn acquire(
+ config: &Config,
+ msg: &str,
+ path: &Path,
+ lock_try: &dyn Fn() -> io::Result<()>,
+ lock_block: &dyn Fn() -> io::Result<()>,
+) -> CargoResult<()> {
+ if try_acquire(path, lock_try)? {
+ return Ok(());
+ }
let msg = format!("waiting for file lock on {}", msg);
config
.shell()
.status_with_color("Blocking", &msg, &style::NOTE)?;
lock_block().with_context(|| format!("failed to lock file: {}", path.display()))?;
- return Ok(());
+ Ok(())
+}
- #[cfg(all(target_os = "linux", not(target_env = "musl")))]
- fn is_on_nfs_mount(path: &Path) -> bool {
- use std::ffi::CString;
- use std::mem;
- use std::os::unix::prelude::*;
+#[cfg(all(target_os = "linux", not(target_env = "musl")))]
+fn is_on_nfs_mount(path: &Path) -> bool {
+ use std::ffi::CString;
+ use std::mem;
+ use std::os::unix::prelude::*;
- let Ok(path) = CString::new(path.as_os_str().as_bytes()) else {
- return false;
- };
+ let Ok(path) = CString::new(path.as_os_str().as_bytes()) else {
+ return false;
+ };
- unsafe {
- let mut buf: libc::statfs = mem::zeroed();
- let r = libc::statfs(path.as_ptr(), &mut buf);
+ unsafe {
+ let mut buf: libc::statfs = mem::zeroed();
+ let r = libc::statfs(path.as_ptr(), &mut buf);
- r == 0 && buf.f_type as u32 == libc::NFS_SUPER_MAGIC as u32
- }
+ r == 0 && buf.f_type as u32 == libc::NFS_SUPER_MAGIC as u32
}
+}
- #[cfg(any(not(target_os = "linux"), target_env = "musl"))]
- fn is_on_nfs_mount(_path: &Path) -> bool {
- false
- }
+#[cfg(any(not(target_os = "linux"), target_env = "musl"))]
+fn is_on_nfs_mount(_path: &Path) -> bool {
+ false
}
#[cfg(unix)]
diff --git a/src/tools/cargo/src/cargo/util/hostname.rs b/src/tools/cargo/src/cargo/util/hostname.rs
new file mode 100644
index 000000000..3f53c9cf6
--- /dev/null
+++ b/src/tools/cargo/src/cargo/util/hostname.rs
@@ -0,0 +1,77 @@
+// Copied from https://github.com/BurntSushi/ripgrep/blob/7099e174acbcbd940f57e4ab4913fee4040c826e/crates/cli/src/hostname.rs
+
+use std::{ffi::OsString, io};
+
+/// Returns the hostname of the current system.
+///
+/// It is unusual, although technically possible, for this routine to return
+/// an error. It is difficult to list out the error conditions, but one such
+/// possibility is platform support.
+///
+/// # Platform specific behavior
+///
+/// On Unix, this returns the result of the `gethostname` function from the
+/// `libc` linked into the program.
+pub fn hostname() -> io::Result<OsString> {
+ #[cfg(unix)]
+ {
+ gethostname()
+ }
+ #[cfg(not(unix))]
+ {
+ Err(io::Error::new(
+ io::ErrorKind::Other,
+ "hostname could not be found on unsupported platform",
+ ))
+ }
+}
+
+#[cfg(unix)]
+fn gethostname() -> io::Result<OsString> {
+ use std::os::unix::ffi::OsStringExt;
+
+ // SAFETY: There don't appear to be any safety requirements for calling
+ // sysconf.
+ let limit = unsafe { libc::sysconf(libc::_SC_HOST_NAME_MAX) };
+ if limit == -1 {
+ // It is in theory possible for sysconf to return -1 for a limit but
+ // *not* set errno, in which case, io::Error::last_os_error is
+ // indeterminate. But untangling that is super annoying because std
+ // doesn't expose any unix-specific APIs for inspecting the errno. (We
+ // could do it ourselves, but it just doesn't seem worth doing?)
+ return Err(io::Error::last_os_error());
+ }
+ let Ok(maxlen) = usize::try_from(limit) else {
+ let msg = format!("host name max limit ({}) overflowed usize", limit);
+ return Err(io::Error::new(io::ErrorKind::Other, msg));
+ };
+ // maxlen here includes the NUL terminator.
+ let mut buf = vec![0; maxlen];
+ // SAFETY: The pointer we give is valid as it is derived directly from a
+ // Vec. Similarly, `maxlen` is the length of our Vec, and is thus valid
+ // to write to.
+ let rc = unsafe { libc::gethostname(buf.as_mut_ptr().cast::<libc::c_char>(), maxlen) };
+ if rc == -1 {
+ return Err(io::Error::last_os_error());
+ }
+ // POSIX says that if the hostname is bigger than `maxlen`, then it may
+ // write a truncate name back that is not necessarily NUL terminated (wtf,
+ // lol). So if we can't find a NUL terminator, then just give up.
+ let Some(zeropos) = buf.iter().position(|&b| b == 0) else {
+ let msg = "could not find NUL terminator in hostname";
+ return Err(io::Error::new(io::ErrorKind::Other, msg));
+ };
+ buf.truncate(zeropos);
+ buf.shrink_to_fit();
+ Ok(OsString::from_vec(buf))
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn print_hostname() {
+ println!("{:?}", hostname());
+ }
+}
diff --git a/src/tools/cargo/src/cargo/util/mod.rs b/src/tools/cargo/src/cargo/util/mod.rs
index b4d14f038..fb4c4b39c 100644
--- a/src/tools/cargo/src/cargo/util/mod.rs
+++ b/src/tools/cargo/src/cargo/util/mod.rs
@@ -14,6 +14,7 @@ pub use self::flock::{FileLock, Filesystem};
pub use self::graph::Graph;
pub use self::hasher::StableHasher;
pub use self::hex::{hash_u64, short_hash, to_hex};
+pub use self::hostname::hostname;
pub use self::into_url::IntoUrl;
pub use self::into_url_with_base::IntoUrlWithBase;
pub(crate) use self::io::LimitErrorReader;
@@ -22,8 +23,7 @@ pub use self::progress::{Progress, ProgressStyle};
pub use self::queue::Queue;
pub use self::restricted_names::validate_package_name;
pub use self::rustc::Rustc;
-pub use self::semver_ext::{OptVersionReq, PartialVersion, RustVersion, VersionExt, VersionReqExt};
-pub use self::to_semver::ToSemver;
+pub use self::semver_ext::{OptVersionReq, RustVersion};
pub use self::vcs::{existing_vcs_repo, FossilRepo, GitRepo, HgRepo, PijulRepo};
pub use self::workspace::{
add_path_args, path_args, print_available_benches, print_available_binaries,
@@ -31,6 +31,7 @@ pub use self::workspace::{
};
pub mod auth;
+pub mod cache_lock;
mod canonical_url;
pub mod command_prelude;
pub mod config;
@@ -45,6 +46,7 @@ mod flock;
pub mod graph;
mod hasher;
pub mod hex;
+mod hostname;
pub mod important_paths;
pub mod interning;
pub mod into_url;
@@ -61,7 +63,6 @@ pub mod restricted_names;
pub mod rustc;
mod semver_ext;
pub mod style;
-pub mod to_semver;
pub mod toml;
pub mod toml_mut;
mod vcs;
diff --git a/src/tools/cargo/src/cargo/util/restricted_names.rs b/src/tools/cargo/src/cargo/util/restricted_names.rs
index 2c3eaa9e1..f61249775 100644
--- a/src/tools/cargo/src/cargo/util/restricted_names.rs
+++ b/src/tools/cargo/src/cargo/util/restricted_names.rs
@@ -120,3 +120,82 @@ pub fn is_windows_reserved_path(path: &Path) -> bool {
pub fn is_glob_pattern<T: AsRef<str>>(name: T) -> bool {
name.as_ref().contains(&['*', '?', '[', ']'][..])
}
+
+/// Validate dir-names and profile names according to RFC 2678.
+pub fn validate_profile_name(name: &str) -> CargoResult<()> {
+ if let Some(ch) = name
+ .chars()
+ .find(|ch| !ch.is_alphanumeric() && *ch != '_' && *ch != '-')
+ {
+ bail!(
+ "invalid character `{}` in profile name `{}`\n\
+ Allowed characters are letters, numbers, underscore, and hyphen.",
+ ch,
+ name
+ );
+ }
+
+ const SEE_DOCS: &str = "See https://doc.rust-lang.org/cargo/reference/profiles.html \
+ for more on configuring profiles.";
+
+ let lower_name = name.to_lowercase();
+ if lower_name == "debug" {
+ bail!(
+ "profile name `{}` is reserved\n\
+ To configure the default development profile, use the name `dev` \
+ as in [profile.dev]\n\
+ {}",
+ name,
+ SEE_DOCS
+ );
+ }
+ if lower_name == "build-override" {
+ bail!(
+ "profile name `{}` is reserved\n\
+ To configure build dependency settings, use [profile.dev.build-override] \
+ and [profile.release.build-override]\n\
+ {}",
+ name,
+ SEE_DOCS
+ );
+ }
+
+ // These are some arbitrary reservations. We have no plans to use
+ // these, but it seems safer to reserve a few just in case we want to
+ // add more built-in profiles in the future. We can also uses special
+ // syntax like cargo:foo if needed. But it is unlikely these will ever
+ // be used.
+ if matches!(
+ lower_name.as_str(),
+ "build"
+ | "check"
+ | "clean"
+ | "config"
+ | "fetch"
+ | "fix"
+ | "install"
+ | "metadata"
+ | "package"
+ | "publish"
+ | "report"
+ | "root"
+ | "run"
+ | "rust"
+ | "rustc"
+ | "rustdoc"
+ | "target"
+ | "tmp"
+ | "uninstall"
+ ) || lower_name.starts_with("cargo")
+ {
+ bail!(
+ "profile name `{}` is reserved\n\
+ Please choose a different name.\n\
+ {}",
+ name,
+ SEE_DOCS
+ );
+ }
+
+ Ok(())
+}
diff --git a/src/tools/cargo/src/cargo/util/rustc.rs b/src/tools/cargo/src/cargo/util/rustc.rs
index d1bb3981d..f51580f29 100644
--- a/src/tools/cargo/src/cargo/util/rustc.rs
+++ b/src/tools/cargo/src/cargo/util/rustc.rs
@@ -28,6 +28,8 @@ pub struct Rustc {
pub version: semver::Version,
/// The host triple (arch-platform-OS), this comes from verbose_version.
pub host: InternedString,
+ /// The rustc full commit hash, this comes from `verbose_version`.
+ pub commit_hash: Option<String>,
cache: Mutex<Cache>,
}
@@ -80,6 +82,17 @@ impl Rustc {
verbose_version
)
})?;
+ let commit_hash = extract("commit-hash: ").ok().map(|hash| {
+ debug_assert!(
+ hash.chars().all(|ch| ch.is_ascii_hexdigit()),
+ "commit hash must be a hex string"
+ );
+ debug_assert!(
+ hash.len() == 40 || hash.len() == 64,
+ "hex string must be generated from sha1 or sha256"
+ );
+ hash.to_string()
+ });
Ok(Rustc {
path,
@@ -88,6 +101,7 @@ impl Rustc {
verbose_version,
version,
host,
+ commit_hash,
cache: Mutex::new(cache),
})
}
diff --git a/src/tools/cargo/src/cargo/util/semver_ext.rs b/src/tools/cargo/src/cargo/util/semver_ext.rs
index 5839d85d2..561cf140e 100644
--- a/src/tools/cargo/src/cargo/util/semver_ext.rs
+++ b/src/tools/cargo/src/cargo/util/semver_ext.rs
@@ -1,52 +1,37 @@
-use semver::{Comparator, Op, Version, VersionReq};
-use serde_untagged::UntaggedEnumVisitor;
use std::fmt::{self, Display};
+use semver::{Op, Version, VersionReq};
+use serde_untagged::UntaggedEnumVisitor;
+
+use crate::util_semver::PartialVersion;
+use crate::util_semver::VersionExt as _;
+
#[derive(PartialEq, Eq, Hash, Clone, Debug)]
pub enum OptVersionReq {
Any,
Req(VersionReq),
/// The exact locked version and the original version requirement.
Locked(Version, VersionReq),
-}
-
-pub trait VersionExt {
- fn is_prerelease(&self) -> bool;
-}
-
-pub trait VersionReqExt {
- fn exact(version: &Version) -> Self;
-}
-
-impl VersionExt for Version {
- fn is_prerelease(&self) -> bool {
- !self.pre.is_empty()
- }
-}
-
-impl VersionReqExt for VersionReq {
- fn exact(version: &Version) -> Self {
- VersionReq {
- comparators: vec![Comparator {
- op: Op::Exact,
- major: version.major,
- minor: Some(version.minor),
- patch: Some(version.patch),
- pre: version.pre.clone(),
- }],
- }
- }
+ /// The exact requested version and the original version requirement.
+ UpdatePrecise(Version, VersionReq),
}
impl OptVersionReq {
pub fn exact(version: &Version) -> Self {
- OptVersionReq::Req(VersionReq::exact(version))
+ OptVersionReq::Req(version.to_exact_req())
+ }
+
+ // Since some registries have allowed crate versions to differ only by build metadata,
+ // A query using OptVersionReq::exact return nondeterministic results.
+ // So we `lock_to` the exact version were interested in.
+ pub fn lock_to_exact(version: &Version) -> Self {
+ OptVersionReq::Locked(version.clone(), version.to_exact_req())
}
pub fn is_exact(&self) -> bool {
match self {
OptVersionReq::Any => false,
- OptVersionReq::Req(req) => {
+ OptVersionReq::Req(req) | OptVersionReq::UpdatePrecise(_, req) => {
req.comparators.len() == 1 && {
let cmp = &req.comparators[0];
cmp.op == Op::Exact && cmp.minor.is_some() && cmp.patch.is_some()
@@ -62,8 +47,18 @@ impl OptVersionReq {
let version = version.clone();
*self = match self {
Any => Locked(version, VersionReq::STAR),
- Req(req) => Locked(version, req.clone()),
- Locked(_, req) => Locked(version, req.clone()),
+ Req(req) | Locked(_, req) | UpdatePrecise(_, req) => Locked(version, req.clone()),
+ };
+ }
+
+ pub fn update_precise(&mut self, version: &Version) {
+ use OptVersionReq::*;
+ let version = version.clone();
+ *self = match self {
+ Any => UpdatePrecise(version, VersionReq::STAR),
+ Req(req) | Locked(_, req) | UpdatePrecise(_, req) => {
+ UpdatePrecise(version, req.clone())
+ }
};
}
@@ -84,10 +79,31 @@ impl OptVersionReq {
OptVersionReq::Any => true,
OptVersionReq::Req(req) => req.matches(version),
OptVersionReq::Locked(v, _) => {
+ // Generally, cargo is of the opinion that semver metadata should be ignored.
+ // If your registry has two versions that only differing metadata you get the bugs you deserve.
+ // We also believe that lock files should ensure reproducibility
+ // and protect against mutations from the registry.
+ // In this circumstance these two goals are in conflict, and we pick reproducibility.
+ // If the lock file tells us that there is a version called `1.0.0+bar` then
+ // we should not silently use `1.0.0+foo` even though they have the same version.
+ v == version
+ }
+ OptVersionReq::UpdatePrecise(v, _) => {
+ // This is used for the `--precise` field of cargo update.
+ //
+ // Unfortunately crates.io allowed versions to differ only
+ // by build metadata. This shouldn't be allowed, but since
+ // it is, this will honor it if requested.
+ //
+ // In that context we treat a requirement that does not have
+ // build metadata as allowing any metadata. But, if a requirement
+ // has build metadata, then we only allow it to match the exact
+ // metadata.
v.major == version.major
&& v.minor == version.minor
&& v.patch == version.patch
&& v.pre == version.pre
+ && (v.build == version.build || v.build.is_empty())
}
}
}
@@ -97,8 +113,9 @@ impl Display for OptVersionReq {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
OptVersionReq::Any => f.write_str("*"),
- OptVersionReq::Req(req) => Display::fmt(req, f),
- OptVersionReq::Locked(_, req) => Display::fmt(req, f),
+ OptVersionReq::Req(req)
+ | OptVersionReq::Locked(_, req)
+ | OptVersionReq::UpdatePrecise(_, req) => Display::fmt(req, f),
}
}
}
@@ -153,207 +170,3 @@ impl Display for RustVersion {
self.0.fmt(f)
}
}
-
-#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Debug)]
-pub struct PartialVersion {
- pub major: u64,
- pub minor: Option<u64>,
- pub patch: Option<u64>,
- pub pre: Option<semver::Prerelease>,
- pub build: Option<semver::BuildMetadata>,
-}
-
-impl PartialVersion {
- pub fn version(&self) -> Option<Version> {
- Some(Version {
- major: self.major,
- minor: self.minor?,
- patch: self.patch?,
- pre: self.pre.clone().unwrap_or_default(),
- build: self.build.clone().unwrap_or_default(),
- })
- }
-
- pub fn caret_req(&self) -> VersionReq {
- VersionReq {
- comparators: vec![Comparator {
- op: semver::Op::Caret,
- major: self.major,
- minor: self.minor,
- patch: self.patch,
- pre: self.pre.as_ref().cloned().unwrap_or_default(),
- }],
- }
- }
-
- /// Check if this matches a version, including build metadata
- ///
- /// Build metadata does not affect version precedence but may be necessary for uniquely
- /// identifying a package.
- pub fn matches(&self, version: &Version) -> bool {
- if !version.pre.is_empty() && self.pre.is_none() {
- // Pre-release versions must be explicitly opted into, if for no other reason than to
- // give us room to figure out and define the semantics
- return false;
- }
- self.major == version.major
- && self.minor.map(|f| f == version.minor).unwrap_or(true)
- && self.patch.map(|f| f == version.patch).unwrap_or(true)
- && self.pre.as_ref().map(|f| f == &version.pre).unwrap_or(true)
- && self
- .build
- .as_ref()
- .map(|f| f == &version.build)
- .unwrap_or(true)
- }
-}
-
-impl From<semver::Version> for PartialVersion {
- fn from(ver: semver::Version) -> Self {
- let pre = if ver.pre.is_empty() {
- None
- } else {
- Some(ver.pre)
- };
- let build = if ver.build.is_empty() {
- None
- } else {
- Some(ver.build)
- };
- Self {
- major: ver.major,
- minor: Some(ver.minor),
- patch: Some(ver.patch),
- pre,
- build,
- }
- }
-}
-
-impl std::str::FromStr for PartialVersion {
- type Err = anyhow::Error;
-
- fn from_str(value: &str) -> Result<Self, Self::Err> {
- if is_req(value) {
- anyhow::bail!("unexpected version requirement, expected a version like \"1.32\"")
- }
- match semver::Version::parse(value) {
- Ok(ver) => Ok(ver.into()),
- Err(_) => {
- // HACK: Leverage `VersionReq` for partial version parsing
- let mut version_req = match semver::VersionReq::parse(value) {
- Ok(req) => req,
- Err(_) if value.contains('-') => {
- anyhow::bail!(
- "unexpected prerelease field, expected a version like \"1.32\""
- )
- }
- Err(_) if value.contains('+') => {
- anyhow::bail!("unexpected build field, expected a version like \"1.32\"")
- }
- Err(_) => anyhow::bail!("expected a version like \"1.32\""),
- };
- assert_eq!(version_req.comparators.len(), 1, "guaranteed by is_req");
- let comp = version_req.comparators.pop().unwrap();
- assert_eq!(comp.op, semver::Op::Caret, "guaranteed by is_req");
- let pre = if comp.pre.is_empty() {
- None
- } else {
- Some(comp.pre)
- };
- Ok(Self {
- major: comp.major,
- minor: comp.minor,
- patch: comp.patch,
- pre,
- build: None,
- })
- }
- }
- }
-}
-
-impl Display for PartialVersion {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- let major = self.major;
- write!(f, "{major}")?;
- if let Some(minor) = self.minor {
- write!(f, ".{minor}")?;
- }
- if let Some(patch) = self.patch {
- write!(f, ".{patch}")?;
- }
- if let Some(pre) = self.pre.as_ref() {
- write!(f, "-{pre}")?;
- }
- if let Some(build) = self.build.as_ref() {
- write!(f, "+{build}")?;
- }
- Ok(())
- }
-}
-
-impl serde::Serialize for PartialVersion {
- fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
- where
- S: serde::Serializer,
- {
- serializer.collect_str(self)
- }
-}
-
-impl<'de> serde::Deserialize<'de> for PartialVersion {
- fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
- where
- D: serde::Deserializer<'de>,
- {
- UntaggedEnumVisitor::new()
- .expecting("SemVer version")
- .string(|value| value.parse().map_err(serde::de::Error::custom))
- .deserialize(deserializer)
- }
-}
-
-fn is_req(value: &str) -> bool {
- let Some(first) = value.chars().next() else {
- return false;
- };
- "<>=^~".contains(first) || value.contains('*') || value.contains(',')
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
-
- #[test]
- fn locked_has_the_same_with_exact() {
- fn test_versions(target_ver: &str, vers: &[&str]) {
- let ver = Version::parse(target_ver).unwrap();
- let exact = OptVersionReq::exact(&ver);
- let mut locked = exact.clone();
- locked.lock_to(&ver);
- for v in vers {
- let v = Version::parse(v).unwrap();
- assert_eq!(exact.matches(&v), locked.matches(&v));
- }
- }
-
- test_versions(
- "1.0.0",
- &["1.0.0", "1.0.1", "0.9.9", "0.10.0", "0.1.0", "1.0.0-pre"],
- );
- test_versions("0.9.0", &["0.9.0", "0.9.1", "1.9.0", "0.0.9", "0.9.0-pre"]);
- test_versions("0.0.2", &["0.0.2", "0.0.1", "0.0.3", "0.0.2-pre"]);
- test_versions(
- "0.1.0-beta2.a",
- &[
- "0.1.0-beta2.a",
- "0.9.1",
- "0.1.0",
- "0.1.1-beta2.a",
- "0.1.0-beta2",
- ],
- );
- test_versions("0.1.0+meta", &["0.1.0", "0.1.0+meta", "0.1.0+any"]);
- }
-}
diff --git a/src/tools/cargo/src/cargo/util/to_semver.rs b/src/tools/cargo/src/cargo/util/to_semver.rs
deleted file mode 100644
index 3cc9e5706..000000000
--- a/src/tools/cargo/src/cargo/util/to_semver.rs
+++ /dev/null
@@ -1,36 +0,0 @@
-use crate::util::errors::CargoResult;
-use semver::Version;
-
-pub trait ToSemver {
- fn to_semver(self) -> CargoResult<Version>;
-}
-
-impl ToSemver for Version {
- fn to_semver(self) -> CargoResult<Version> {
- Ok(self)
- }
-}
-
-impl<'a> ToSemver for &'a str {
- fn to_semver(self) -> CargoResult<Version> {
- match Version::parse(self.trim()) {
- Ok(v) => Ok(v),
- Err(..) => Err(anyhow::format_err!(
- "cannot parse '{}' as a SemVer version",
- self
- )),
- }
- }
-}
-
-impl<'a> ToSemver for &'a String {
- fn to_semver(self) -> CargoResult<Version> {
- (**self).to_semver()
- }
-}
-
-impl<'a> ToSemver for &'a Version {
- fn to_semver(self) -> CargoResult<Version> {
- Ok(self.clone())
- }
-}
diff --git a/src/tools/cargo/src/cargo/util/toml/embedded.rs b/src/tools/cargo/src/cargo/util/toml/embedded.rs
index 482268923..4c57195d4 100644
--- a/src/tools/cargo/src/cargo/util/toml/embedded.rs
+++ b/src/tools/cargo/src/cargo/util/toml/embedded.rs
@@ -6,11 +6,9 @@ use crate::Config;
const DEFAULT_EDITION: crate::core::features::Edition =
crate::core::features::Edition::LATEST_STABLE;
-const DEFAULT_VERSION: &str = "0.0.0";
-const DEFAULT_PUBLISH: bool = false;
const AUTO_FIELDS: &[&str] = &["autobins", "autoexamples", "autotests", "autobenches"];
-pub fn expand_manifest(
+pub(super) fn expand_manifest(
content: &str,
path: &std::path::Path,
config: &Config,
@@ -123,9 +121,6 @@ fn expand_manifest_(
package
.entry("name".to_owned())
.or_insert(toml::Value::String(name));
- package
- .entry("version".to_owned())
- .or_insert_with(|| toml::Value::String(DEFAULT_VERSION.to_owned()));
package.entry("edition".to_owned()).or_insert_with(|| {
let _ = config.shell().warn(format_args!(
"`package.edition` is unspecified, defaulting to `{}`",
@@ -136,9 +131,6 @@ fn expand_manifest_(
package
.entry("build".to_owned())
.or_insert_with(|| toml::Value::Boolean(false));
- package
- .entry("publish".to_owned())
- .or_insert_with(|| toml::Value::Boolean(DEFAULT_PUBLISH));
for field in AUTO_FIELDS {
package
.entry(field.to_owned())
@@ -337,7 +329,7 @@ impl DocFragment {
}
#[derive(Clone, Copy, PartialEq, Debug)]
-pub enum CommentKind {
+enum CommentKind {
Line,
Block,
}
@@ -621,8 +613,6 @@ autotests = false
build = false
edition = "2021"
name = "test-"
-publish = false
-version = "0.0.0"
[profile.release]
strip = true
@@ -651,8 +641,6 @@ autotests = false
build = false
edition = "2021"
name = "test-"
-publish = false
-version = "0.0.0"
[profile.release]
strip = true
diff --git a/src/tools/cargo/src/cargo/util/toml/mod.rs b/src/tools/cargo/src/cargo/util/toml/mod.rs
index 2e730b4e9..cb841476b 100644
--- a/src/tools/cargo/src/cargo/util/toml/mod.rs
+++ b/src/tools/cargo/src/cargo/util/toml/mod.rs
@@ -1,6 +1,5 @@
use std::collections::{BTreeMap, BTreeSet, HashMap};
use std::ffi::OsStr;
-use std::fmt::{self, Display, Write};
use std::path::{Path, PathBuf};
use std::rc::Rc;
use std::str::{self, FromStr};
@@ -10,10 +9,6 @@ use cargo_platform::Platform;
use cargo_util::paths;
use itertools::Itertools;
use lazycell::LazyCell;
-use serde::de::{self, IntoDeserializer as _, Unexpected};
-use serde::ser;
-use serde::{Deserialize, Serialize};
-use serde_untagged::UntaggedEnumVisitor;
use tracing::{debug, trace};
use url::Url;
@@ -28,12 +23,14 @@ use crate::core::{GitReference, PackageIdSpec, SourceId, WorkspaceConfig, Worksp
use crate::sources::{CRATES_IO_INDEX, CRATES_IO_REGISTRY};
use crate::util::errors::{CargoResult, ManifestError};
use crate::util::interning::InternedString;
+use crate::util::restricted_names;
use crate::util::{
self, config::ConfigRelativePath, validate_package_name, Config, IntoUrl, OptVersionReq,
RustVersion,
};
-pub mod embedded;
+mod embedded;
+pub mod schema;
mod targets;
use self::targets::targets;
@@ -100,7 +97,7 @@ fn read_manifest_from_str(
let mut unused = BTreeSet::new();
let deserializer = toml::de::Deserializer::new(contents);
- let manifest: TomlManifest = serde_ignored::deserialize(deserializer, |path| {
+ let manifest: schema::TomlManifest = serde_ignored::deserialize(deserializer, |path| {
let mut key = String::new();
stringify(&mut key, &path);
unused.insert(key);
@@ -114,7 +111,6 @@ fn read_manifest_from_str(
}
};
- let manifest = Rc::new(manifest);
if let Some(deps) = manifest
.workspace
.as_ref()
@@ -130,8 +126,13 @@ fn read_manifest_from_str(
}
}
return if manifest.project.is_some() || manifest.package.is_some() {
- let (mut manifest, paths) =
- TomlManifest::to_real_manifest(&manifest, embedded, source_id, package_root, config)?;
+ let (mut manifest, paths) = schema::TomlManifest::to_real_manifest(
+ manifest,
+ embedded,
+ source_id,
+ package_root,
+ config,
+ )?;
add_unused(manifest.warnings_mut());
if manifest.targets().iter().all(|t| t.is_custom_build()) {
bail!(
@@ -143,7 +144,7 @@ fn read_manifest_from_str(
Ok((EitherManifest::Real(manifest), paths))
} else {
let (mut m, paths) =
- TomlManifest::to_virtual_manifest(&manifest, source_id, package_root, config)?;
+ schema::TomlManifest::to_virtual_manifest(manifest, source_id, package_root, config)?;
add_unused(m.warnings_mut());
Ok((EitherManifest::Virtual(m), paths))
};
@@ -174,11 +175,6 @@ fn read_manifest_from_str(
}
}
-pub fn parse_document(toml: &str, _file: &Path, _config: &Config) -> CargoResult<toml::Table> {
- // At the moment, no compatibility checks are needed.
- toml.parse().map_err(Into::into)
-}
-
/// Warn about paths that have been deprecated and may conflict.
fn warn_on_deprecated(new_path: &str, name: &str, kind: &str, warnings: &mut Vec<String>) {
let old_path = new_path.replace("-", "_");
@@ -188,1421 +184,7 @@ fn warn_on_deprecated(new_path: &str, name: &str, kind: &str, warnings: &mut Vec
))
}
-type TomlLibTarget = TomlTarget;
-type TomlBinTarget = TomlTarget;
-type TomlExampleTarget = TomlTarget;
-type TomlTestTarget = TomlTarget;
-type TomlBenchTarget = TomlTarget;
-
-#[derive(Clone, Debug, Serialize)]
-#[serde(untagged)]
-pub enum TomlDependency<P: Clone = String> {
- /// In the simple format, only a version is specified, eg.
- /// `package = "<version>"`
- Simple(String),
- /// The simple format is equivalent to a detailed dependency
- /// specifying only a version, eg.
- /// `package = { version = "<version>" }`
- Detailed(DetailedTomlDependency<P>),
-}
-
-impl<'de, P: Deserialize<'de> + Clone> de::Deserialize<'de> for TomlDependency<P> {
- fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
- where
- D: de::Deserializer<'de>,
- {
- UntaggedEnumVisitor::new()
- .expecting(
- "a version string like \"0.9.8\" or a \
- detailed dependency like { version = \"0.9.8\" }",
- )
- .string(|value| Ok(TomlDependency::Simple(value.to_owned())))
- .map(|value| value.deserialize().map(TomlDependency::Detailed))
- .deserialize(deserializer)
- }
-}
-
-impl TomlDependency {
- fn unused_keys(&self) -> Vec<String> {
- match self {
- TomlDependency::Simple(_) => vec![],
- TomlDependency::Detailed(detailed) => detailed.other.keys().cloned().collect(),
- }
- }
-}
-
-pub trait ResolveToPath {
- fn resolve(&self, config: &Config) -> PathBuf;
-}
-
-impl ResolveToPath for String {
- fn resolve(&self, _: &Config) -> PathBuf {
- self.into()
- }
-}
-
-impl ResolveToPath for ConfigRelativePath {
- fn resolve(&self, c: &Config) -> PathBuf {
- self.resolve_path(c)
- }
-}
-
-#[derive(Deserialize, Serialize, Clone, Debug)]
-#[serde(rename_all = "kebab-case")]
-pub struct DetailedTomlDependency<P: Clone = String> {
- version: Option<String>,
- registry: Option<String>,
- /// The URL of the `registry` field.
- /// This is an internal implementation detail. When Cargo creates a
- /// package, it replaces `registry` with `registry-index` so that the
- /// manifest contains the correct URL. All users won't have the same
- /// registry names configured, so Cargo can't rely on just the name for
- /// crates published by other users.
- registry_index: Option<String>,
- // `path` is relative to the file it appears in. If that's a `Cargo.toml`, it'll be relative to
- // that TOML file, and if it's a `.cargo/config` file, it'll be relative to that file.
- path: Option<P>,
- git: Option<String>,
- branch: Option<String>,
- tag: Option<String>,
- rev: Option<String>,
- features: Option<Vec<String>>,
- optional: Option<bool>,
- default_features: Option<bool>,
- #[serde(rename = "default_features")]
- default_features2: Option<bool>,
- package: Option<String>,
- public: Option<bool>,
-
- /// One or more of `bin`, `cdylib`, `staticlib`, `bin:<name>`.
- artifact: Option<StringOrVec>,
- /// If set, the artifact should also be a dependency
- lib: Option<bool>,
- /// A platform name, like `x86_64-apple-darwin`
- target: Option<String>,
- /// This is here to provide a way to see the "unused manifest keys" when deserializing
- #[serde(skip_serializing)]
- #[serde(flatten)]
- other: BTreeMap<String, toml::Value>,
-}
-
-// Explicit implementation so we avoid pulling in P: Default
-impl<P: Clone> Default for DetailedTomlDependency<P> {
- fn default() -> Self {
- Self {
- version: Default::default(),
- registry: Default::default(),
- registry_index: Default::default(),
- path: Default::default(),
- git: Default::default(),
- branch: Default::default(),
- tag: Default::default(),
- rev: Default::default(),
- features: Default::default(),
- optional: Default::default(),
- default_features: Default::default(),
- default_features2: Default::default(),
- package: Default::default(),
- public: Default::default(),
- artifact: Default::default(),
- lib: Default::default(),
- target: Default::default(),
- other: Default::default(),
- }
- }
-}
-
-/// This type is used to deserialize `Cargo.toml` files.
-#[derive(Debug, Deserialize, Serialize)]
-#[serde(rename_all = "kebab-case")]
-pub struct TomlManifest {
- cargo_features: Option<Vec<String>>,
- package: Option<Box<TomlPackage>>,
- project: Option<Box<TomlPackage>>,
- profile: Option<TomlProfiles>,
- lib: Option<TomlLibTarget>,
- bin: Option<Vec<TomlBinTarget>>,
- example: Option<Vec<TomlExampleTarget>>,
- test: Option<Vec<TomlTestTarget>>,
- bench: Option<Vec<TomlTestTarget>>,
- dependencies: Option<BTreeMap<String, MaybeWorkspaceDependency>>,
- dev_dependencies: Option<BTreeMap<String, MaybeWorkspaceDependency>>,
- #[serde(rename = "dev_dependencies")]
- dev_dependencies2: Option<BTreeMap<String, MaybeWorkspaceDependency>>,
- build_dependencies: Option<BTreeMap<String, MaybeWorkspaceDependency>>,
- #[serde(rename = "build_dependencies")]
- build_dependencies2: Option<BTreeMap<String, MaybeWorkspaceDependency>>,
- features: Option<BTreeMap<InternedString, Vec<InternedString>>>,
- target: Option<BTreeMap<String, TomlPlatform>>,
- replace: Option<BTreeMap<String, TomlDependency>>,
- patch: Option<BTreeMap<String, BTreeMap<String, TomlDependency>>>,
- workspace: Option<TomlWorkspace>,
- badges: Option<MaybeWorkspaceBtreeMap>,
- lints: Option<MaybeWorkspaceLints>,
-}
-
-#[derive(Deserialize, Serialize, Clone, Debug, Default)]
-pub struct TomlProfiles(BTreeMap<InternedString, TomlProfile>);
-
-impl TomlProfiles {
- pub fn get_all(&self) -> &BTreeMap<InternedString, TomlProfile> {
- &self.0
- }
-
- pub fn get(&self, name: &str) -> Option<&TomlProfile> {
- self.0.get(name)
- }
-
- /// Checks syntax validity and unstable feature gate for each profile.
- ///
- /// It's a bit unfortunate both `-Z` flags and `cargo-features` are required,
- /// because profiles can now be set in either `Cargo.toml` or `config.toml`.
- pub fn validate(
- &self,
- cli_unstable: &CliUnstable,
- features: &Features,
- warnings: &mut Vec<String>,
- ) -> CargoResult<()> {
- for (name, profile) in &self.0 {
- profile.validate(name, cli_unstable, features, warnings)?;
- }
- Ok(())
- }
-}
-
-#[derive(Clone, Debug, Eq, PartialEq)]
-pub struct TomlOptLevel(pub String);
-
-impl<'de> de::Deserialize<'de> for TomlOptLevel {
- fn deserialize<D>(d: D) -> Result<TomlOptLevel, D::Error>
- where
- D: de::Deserializer<'de>,
- {
- use serde::de::Error as _;
- UntaggedEnumVisitor::new()
- .expecting("an optimization level")
- .i64(|value| Ok(TomlOptLevel(value.to_string())))
- .string(|value| {
- if value == "s" || value == "z" {
- Ok(TomlOptLevel(value.to_string()))
- } else {
- Err(serde_untagged::de::Error::custom(format!(
- "must be `0`, `1`, `2`, `3`, `s` or `z`, \
- but found the string: \"{}\"",
- value
- )))
- }
- })
- .deserialize(d)
- }
-}
-
-impl ser::Serialize for TomlOptLevel {
- fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
- where
- S: ser::Serializer,
- {
- match self.0.parse::<u32>() {
- Ok(n) => n.serialize(serializer),
- Err(_) => self.0.serialize(serializer),
- }
- }
-}
-
-#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, PartialOrd, Ord)]
-pub enum TomlDebugInfo {
- None,
- LineDirectivesOnly,
- LineTablesOnly,
- Limited,
- Full,
-}
-
-impl ser::Serialize for TomlDebugInfo {
- fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
- where
- S: ser::Serializer,
- {
- match self {
- Self::None => 0.serialize(serializer),
- Self::LineDirectivesOnly => "line-directives-only".serialize(serializer),
- Self::LineTablesOnly => "line-tables-only".serialize(serializer),
- Self::Limited => 1.serialize(serializer),
- Self::Full => 2.serialize(serializer),
- }
- }
-}
-
-impl<'de> de::Deserialize<'de> for TomlDebugInfo {
- fn deserialize<D>(d: D) -> Result<TomlDebugInfo, D::Error>
- where
- D: de::Deserializer<'de>,
- {
- use serde::de::Error as _;
- let expecting = "a boolean, 0, 1, 2, \"line-tables-only\", or \"line-directives-only\"";
- UntaggedEnumVisitor::new()
- .expecting(expecting)
- .bool(|value| {
- Ok(if value {
- TomlDebugInfo::Full
- } else {
- TomlDebugInfo::None
- })
- })
- .i64(|value| {
- let debuginfo = match value {
- 0 => TomlDebugInfo::None,
- 1 => TomlDebugInfo::Limited,
- 2 => TomlDebugInfo::Full,
- _ => {
- return Err(serde_untagged::de::Error::invalid_value(
- Unexpected::Signed(value),
- &expecting,
- ))
- }
- };
- Ok(debuginfo)
- })
- .string(|value| {
- let debuginfo = match value {
- "none" => TomlDebugInfo::None,
- "limited" => TomlDebugInfo::Limited,
- "full" => TomlDebugInfo::Full,
- "line-directives-only" => TomlDebugInfo::LineDirectivesOnly,
- "line-tables-only" => TomlDebugInfo::LineTablesOnly,
- _ => {
- return Err(serde_untagged::de::Error::invalid_value(
- Unexpected::Str(value),
- &expecting,
- ))
- }
- };
- Ok(debuginfo)
- })
- .deserialize(d)
- }
-}
-
-impl Display for TomlDebugInfo {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- match self {
- TomlDebugInfo::None => f.write_char('0'),
- TomlDebugInfo::Limited => f.write_char('1'),
- TomlDebugInfo::Full => f.write_char('2'),
- TomlDebugInfo::LineDirectivesOnly => f.write_str("line-directives-only"),
- TomlDebugInfo::LineTablesOnly => f.write_str("line-tables-only"),
- }
- }
-}
-
-#[derive(Deserialize, Serialize, Clone, Debug, Default, Eq, PartialEq)]
-#[serde(default, rename_all = "kebab-case")]
-pub struct TomlProfile {
- pub opt_level: Option<TomlOptLevel>,
- pub lto: Option<StringOrBool>,
- pub codegen_backend: Option<InternedString>,
- pub codegen_units: Option<u32>,
- pub debug: Option<TomlDebugInfo>,
- pub split_debuginfo: Option<String>,
- pub debug_assertions: Option<bool>,
- pub rpath: Option<bool>,
- pub panic: Option<String>,
- pub overflow_checks: Option<bool>,
- pub incremental: Option<bool>,
- pub dir_name: Option<InternedString>,
- pub inherits: Option<InternedString>,
- pub strip: Option<StringOrBool>,
- // Note that `rustflags` is used for the cargo-feature `profile_rustflags`
- pub rustflags: Option<Vec<InternedString>>,
- // These two fields must be last because they are sub-tables, and TOML
- // requires all non-tables to be listed first.
- pub package: Option<BTreeMap<ProfilePackageSpec, TomlProfile>>,
- pub build_override: Option<Box<TomlProfile>>,
-}
-
-#[derive(Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]
-pub enum ProfilePackageSpec {
- Spec(PackageIdSpec),
- All,
-}
-
-impl ser::Serialize for ProfilePackageSpec {
- fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
- where
- S: ser::Serializer,
- {
- self.to_string().serialize(s)
- }
-}
-
-impl<'de> de::Deserialize<'de> for ProfilePackageSpec {
- fn deserialize<D>(d: D) -> Result<ProfilePackageSpec, D::Error>
- where
- D: de::Deserializer<'de>,
- {
- let string = String::deserialize(d)?;
- if string == "*" {
- Ok(ProfilePackageSpec::All)
- } else {
- PackageIdSpec::parse(&string)
- .map_err(de::Error::custom)
- .map(ProfilePackageSpec::Spec)
- }
- }
-}
-
-impl fmt::Display for ProfilePackageSpec {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- match self {
- ProfilePackageSpec::Spec(spec) => spec.fmt(f),
- ProfilePackageSpec::All => f.write_str("*"),
- }
- }
-}
-
-impl TomlProfile {
- /// Checks stytax validity and unstable feature gate for a given profile.
- pub fn validate(
- &self,
- name: &str,
- cli_unstable: &CliUnstable,
- features: &Features,
- warnings: &mut Vec<String>,
- ) -> CargoResult<()> {
- self.validate_profile(name, cli_unstable, features)?;
- if let Some(ref profile) = self.build_override {
- profile.validate_override("build-override")?;
- profile.validate_profile(&format!("{name}.build-override"), cli_unstable, features)?;
- }
- if let Some(ref packages) = self.package {
- for (override_name, profile) in packages {
- profile.validate_override("package")?;
- profile.validate_profile(
- &format!("{name}.package.{override_name}"),
- cli_unstable,
- features,
- )?;
- }
- }
-
- // Profile name validation
- Self::validate_name(name)?;
-
- if let Some(dir_name) = self.dir_name {
- // This is disabled for now, as we would like to stabilize named
- // profiles without this, and then decide in the future if it is
- // needed. This helps simplify the UI a little.
- bail!(
- "dir-name=\"{}\" in profile `{}` is not currently allowed, \
- directory names are tied to the profile name for custom profiles",
- dir_name,
- name
- );
- }
-
- // `inherits` validation
- if matches!(self.inherits.map(|s| s.as_str()), Some("debug")) {
- bail!(
- "profile.{}.inherits=\"debug\" should be profile.{}.inherits=\"dev\"",
- name,
- name
- );
- }
-
- match name {
- "doc" => {
- warnings.push("profile `doc` is deprecated and has no effect".to_string());
- }
- "test" | "bench" => {
- if self.panic.is_some() {
- warnings.push(format!("`panic` setting is ignored for `{}` profile", name))
- }
- }
- _ => {}
- }
-
- if let Some(panic) = &self.panic {
- if panic != "unwind" && panic != "abort" {
- bail!(
- "`panic` setting of `{}` is not a valid setting, \
- must be `unwind` or `abort`",
- panic
- );
- }
- }
-
- if let Some(StringOrBool::String(arg)) = &self.lto {
- if arg == "true" || arg == "false" {
- bail!(
- "`lto` setting of string `\"{arg}\"` for `{name}` profile is not \
- a valid setting, must be a boolean (`true`/`false`) or a string \
- (`\"thin\"`/`\"fat\"`/`\"off\"`) or omitted.",
- );
- }
- }
-
- Ok(())
- }
-
- /// Validate dir-names and profile names according to RFC 2678.
- pub fn validate_name(name: &str) -> CargoResult<()> {
- if let Some(ch) = name
- .chars()
- .find(|ch| !ch.is_alphanumeric() && *ch != '_' && *ch != '-')
- {
- bail!(
- "invalid character `{}` in profile name `{}`\n\
- Allowed characters are letters, numbers, underscore, and hyphen.",
- ch,
- name
- );
- }
-
- const SEE_DOCS: &str = "See https://doc.rust-lang.org/cargo/reference/profiles.html \
- for more on configuring profiles.";
-
- let lower_name = name.to_lowercase();
- if lower_name == "debug" {
- bail!(
- "profile name `{}` is reserved\n\
- To configure the default development profile, use the name `dev` \
- as in [profile.dev]\n\
- {}",
- name,
- SEE_DOCS
- );
- }
- if lower_name == "build-override" {
- bail!(
- "profile name `{}` is reserved\n\
- To configure build dependency settings, use [profile.dev.build-override] \
- and [profile.release.build-override]\n\
- {}",
- name,
- SEE_DOCS
- );
- }
-
- // These are some arbitrary reservations. We have no plans to use
- // these, but it seems safer to reserve a few just in case we want to
- // add more built-in profiles in the future. We can also uses special
- // syntax like cargo:foo if needed. But it is unlikely these will ever
- // be used.
- if matches!(
- lower_name.as_str(),
- "build"
- | "check"
- | "clean"
- | "config"
- | "fetch"
- | "fix"
- | "install"
- | "metadata"
- | "package"
- | "publish"
- | "report"
- | "root"
- | "run"
- | "rust"
- | "rustc"
- | "rustdoc"
- | "target"
- | "tmp"
- | "uninstall"
- ) || lower_name.starts_with("cargo")
- {
- bail!(
- "profile name `{}` is reserved\n\
- Please choose a different name.\n\
- {}",
- name,
- SEE_DOCS
- );
- }
-
- Ok(())
- }
-
- /// Validates a profile.
- ///
- /// This is a shallow check, which is reused for the profile itself and any overrides.
- fn validate_profile(
- &self,
- name: &str,
- cli_unstable: &CliUnstable,
- features: &Features,
- ) -> CargoResult<()> {
- if let Some(codegen_backend) = &self.codegen_backend {
- match (
- features.require(Feature::codegen_backend()),
- cli_unstable.codegen_backend,
- ) {
- (Err(e), false) => return Err(e),
- _ => {}
- }
-
- if codegen_backend.contains(|c: char| !c.is_ascii_alphanumeric() && c != '_') {
- bail!(
- "`profile.{}.codegen-backend` setting of `{}` is not a valid backend name.",
- name,
- codegen_backend,
- );
- }
- }
- if self.rustflags.is_some() {
- match (
- features.require(Feature::profile_rustflags()),
- cli_unstable.profile_rustflags,
- ) {
- (Err(e), false) => return Err(e),
- _ => {}
- }
- }
- Ok(())
- }
-
- /// Validation that is specific to an override.
- fn validate_override(&self, which: &str) -> CargoResult<()> {
- if self.package.is_some() {
- bail!("package-specific profiles cannot be nested");
- }
- if self.build_override.is_some() {
- bail!("build-override profiles cannot be nested");
- }
- if self.panic.is_some() {
- bail!("`panic` may not be specified in a `{}` profile", which)
- }
- if self.lto.is_some() {
- bail!("`lto` may not be specified in a `{}` profile", which)
- }
- if self.rpath.is_some() {
- bail!("`rpath` may not be specified in a `{}` profile", which)
- }
- Ok(())
- }
-
- /// Overwrite self's values with the given profile.
- pub fn merge(&mut self, profile: &TomlProfile) {
- if let Some(v) = &profile.opt_level {
- self.opt_level = Some(v.clone());
- }
-
- if let Some(v) = &profile.lto {
- self.lto = Some(v.clone());
- }
-
- if let Some(v) = profile.codegen_backend {
- self.codegen_backend = Some(v);
- }
-
- if let Some(v) = profile.codegen_units {
- self.codegen_units = Some(v);
- }
-
- if let Some(v) = profile.debug {
- self.debug = Some(v);
- }
-
- if let Some(v) = profile.debug_assertions {
- self.debug_assertions = Some(v);
- }
-
- if let Some(v) = &profile.split_debuginfo {
- self.split_debuginfo = Some(v.clone());
- }
-
- if let Some(v) = profile.rpath {
- self.rpath = Some(v);
- }
-
- if let Some(v) = &profile.panic {
- self.panic = Some(v.clone());
- }
-
- if let Some(v) = profile.overflow_checks {
- self.overflow_checks = Some(v);
- }
-
- if let Some(v) = profile.incremental {
- self.incremental = Some(v);
- }
-
- if let Some(v) = &profile.rustflags {
- self.rustflags = Some(v.clone());
- }
-
- if let Some(other_package) = &profile.package {
- match &mut self.package {
- Some(self_package) => {
- for (spec, other_pkg_profile) in other_package {
- match self_package.get_mut(spec) {
- Some(p) => p.merge(other_pkg_profile),
- None => {
- self_package.insert(spec.clone(), other_pkg_profile.clone());
- }
- }
- }
- }
- None => self.package = Some(other_package.clone()),
- }
- }
-
- if let Some(other_bo) = &profile.build_override {
- match &mut self.build_override {
- Some(self_bo) => self_bo.merge(other_bo),
- None => self.build_override = Some(other_bo.clone()),
- }
- }
-
- if let Some(v) = &profile.inherits {
- self.inherits = Some(*v);
- }
-
- if let Some(v) = &profile.dir_name {
- self.dir_name = Some(*v);
- }
-
- if let Some(v) = &profile.strip {
- self.strip = Some(v.clone());
- }
- }
-}
-
-/// A StringOrVec can be parsed from either a TOML string or array,
-/// but is always stored as a vector.
-#[derive(Clone, Debug, Serialize, Eq, PartialEq, PartialOrd, Ord)]
-pub struct StringOrVec(Vec<String>);
-
-impl<'de> de::Deserialize<'de> for StringOrVec {
- fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
- where
- D: de::Deserializer<'de>,
- {
- UntaggedEnumVisitor::new()
- .expecting("string or list of strings")
- .string(|value| Ok(StringOrVec(vec![value.to_owned()])))
- .seq(|value| value.deserialize().map(StringOrVec))
- .deserialize(deserializer)
- }
-}
-
-impl StringOrVec {
- pub fn iter<'a>(&'a self) -> std::slice::Iter<'a, String> {
- self.0.iter()
- }
-}
-
-#[derive(Clone, Debug, Serialize, Eq, PartialEq)]
-#[serde(untagged)]
-pub enum StringOrBool {
- String(String),
- Bool(bool),
-}
-
-impl<'de> Deserialize<'de> for StringOrBool {
- fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
- where
- D: de::Deserializer<'de>,
- {
- UntaggedEnumVisitor::new()
- .bool(|b| Ok(StringOrBool::Bool(b)))
- .string(|s| Ok(StringOrBool::String(s.to_owned())))
- .deserialize(deserializer)
- }
-}
-
-#[derive(PartialEq, Clone, Debug, Serialize)]
-#[serde(untagged)]
-pub enum VecStringOrBool {
- VecString(Vec<String>),
- Bool(bool),
-}
-
-impl<'de> de::Deserialize<'de> for VecStringOrBool {
- fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
- where
- D: de::Deserializer<'de>,
- {
- UntaggedEnumVisitor::new()
- .expecting("a boolean or vector of strings")
- .bool(|value| Ok(VecStringOrBool::Bool(value)))
- .seq(|value| value.deserialize().map(VecStringOrBool::VecString))
- .deserialize(deserializer)
- }
-}
-
-fn version_trim_whitespace<'de, D>(deserializer: D) -> Result<MaybeWorkspaceSemverVersion, D::Error>
-where
- D: de::Deserializer<'de>,
-{
- UntaggedEnumVisitor::new()
- .expecting("SemVer version")
- .string(
- |value| match value.trim().parse().map_err(de::Error::custom) {
- Ok(parsed) => Ok(MaybeWorkspace::Defined(parsed)),
- Err(e) => Err(e),
- },
- )
- .map(|value| value.deserialize().map(MaybeWorkspace::Workspace))
- .deserialize(deserializer)
-}
-
-/// This Trait exists to make [`MaybeWorkspace::Workspace`] generic. It makes deserialization of
-/// [`MaybeWorkspace`] much easier, as well as making error messages for
-/// [`MaybeWorkspace::resolve`] much nicer
-///
-/// Implementors should have a field `workspace` with the type of `bool`. It is used to ensure
-/// `workspace` is not `false` in a `Cargo.toml`
-pub trait WorkspaceInherit {
- /// This is the workspace table that is being inherited from.
- /// For example `[workspace.dependencies]` would be the table "dependencies"
- fn inherit_toml_table(&self) -> &str;
-
- /// This is used to output the value of the implementors `workspace` field
- fn workspace(&self) -> bool;
-}
-
-/// An enum that allows for inheriting keys from a workspace in a Cargo.toml.
-#[derive(Serialize, Copy, Clone, Debug)]
-#[serde(untagged)]
-pub enum MaybeWorkspace<T, W: WorkspaceInherit> {
- /// The "defined" type, or the type that that is used when not inheriting from a workspace.
- Defined(T),
- /// The type when inheriting from a workspace.
- Workspace(W),
-}
-
-impl<T, W: WorkspaceInherit> MaybeWorkspace<T, W> {
- fn resolve<'a>(
- self,
- label: &str,
- get_ws_inheritable: impl FnOnce() -> CargoResult<T>,
- ) -> CargoResult<T> {
- match self {
- MaybeWorkspace::Defined(value) => Ok(value),
- MaybeWorkspace::Workspace(w) => get_ws_inheritable().with_context(|| {
- format!(
- "error inheriting `{label}` from workspace root manifest's `workspace.{}.{label}`",
- w.inherit_toml_table(),
- )
- }),
- }
- }
-
- fn resolve_with_self<'a>(
- self,
- label: &str,
- get_ws_inheritable: impl FnOnce(&W) -> CargoResult<T>,
- ) -> CargoResult<T> {
- match self {
- MaybeWorkspace::Defined(value) => Ok(value),
- MaybeWorkspace::Workspace(w) => get_ws_inheritable(&w).with_context(|| {
- format!(
- "error inheriting `{label}` from workspace root manifest's `workspace.{}.{label}`",
- w.inherit_toml_table(),
- )
- }),
- }
- }
-
- fn as_defined(&self) -> Option<&T> {
- match self {
- MaybeWorkspace::Workspace(_) => None,
- MaybeWorkspace::Defined(defined) => Some(defined),
- }
- }
-}
-
-type MaybeWorkspaceDependency = MaybeWorkspace<TomlDependency, TomlWorkspaceDependency>;
-
-impl<'de> de::Deserialize<'de> for MaybeWorkspaceDependency {
- fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
- where
- D: de::Deserializer<'de>,
- {
- let value = serde_value::Value::deserialize(deserializer)?;
-
- if let Ok(w) = TomlWorkspaceDependency::deserialize(serde_value::ValueDeserializer::<
- D::Error,
- >::new(value.clone()))
- {
- return if w.workspace() {
- Ok(MaybeWorkspace::Workspace(w))
- } else {
- Err(de::Error::custom("`workspace` cannot be false"))
- };
- }
- TomlDependency::deserialize(serde_value::ValueDeserializer::<D::Error>::new(value))
- .map(MaybeWorkspace::Defined)
- }
-}
-
-impl MaybeWorkspaceDependency {
- fn unused_keys(&self) -> Vec<String> {
- match self {
- MaybeWorkspaceDependency::Defined(d) => d.unused_keys(),
- MaybeWorkspaceDependency::Workspace(w) => w.other.keys().cloned().collect(),
- }
- }
-}
-
-#[derive(Deserialize, Serialize, Clone, Debug)]
-#[serde(rename_all = "kebab-case")]
-pub struct TomlWorkspaceDependency {
- workspace: bool,
- features: Option<Vec<String>>,
- default_features: Option<bool>,
- #[serde(rename = "default_features")]
- default_features2: Option<bool>,
- optional: Option<bool>,
- /// This is here to provide a way to see the "unused manifest keys" when deserializing
- #[serde(skip_serializing)]
- #[serde(flatten)]
- other: BTreeMap<String, toml::Value>,
-}
-
-impl WorkspaceInherit for TomlWorkspaceDependency {
- fn inherit_toml_table(&self) -> &str {
- "dependencies"
- }
-
- fn workspace(&self) -> bool {
- self.workspace
- }
-}
-
-impl TomlWorkspaceDependency {
- fn resolve<'a>(
- &self,
- name: &str,
- inheritable: impl FnOnce() -> CargoResult<&'a InheritableFields>,
- cx: &mut Context<'_, '_>,
- ) -> CargoResult<TomlDependency> {
- fn default_features_msg(label: &str, ws_def_feat: Option<bool>, cx: &mut Context<'_, '_>) {
- let ws_def_feat = match ws_def_feat {
- Some(true) => "true",
- Some(false) => "false",
- None => "not specified",
- };
- cx.warnings.push(format!(
- "`default-features` is ignored for {label}, since `default-features` was \
- {ws_def_feat} for `workspace.dependencies.{label}`, \
- this could become a hard error in the future"
- ))
- }
- if self.default_features.is_some() && self.default_features2.is_some() {
- warn_on_deprecated("default-features", name, "dependency", cx.warnings);
- }
- inheritable()?.get_dependency(name, cx.root).map(|d| {
- match d {
- TomlDependency::Simple(s) => {
- if let Some(false) = self.default_features.or(self.default_features2) {
- default_features_msg(name, None, cx);
- }
- if self.optional.is_some() || self.features.is_some() {
- TomlDependency::Detailed(DetailedTomlDependency {
- version: Some(s),
- optional: self.optional,
- features: self.features.clone(),
- ..Default::default()
- })
- } else {
- TomlDependency::Simple(s)
- }
- }
- TomlDependency::Detailed(d) => {
- let mut d = d.clone();
- match (
- self.default_features.or(self.default_features2),
- d.default_features.or(d.default_features2),
- ) {
- // member: default-features = true and
- // workspace: default-features = false should turn on
- // default-features
- (Some(true), Some(false)) => {
- d.default_features = Some(true);
- }
- // member: default-features = false and
- // workspace: default-features = true should ignore member
- // default-features
- (Some(false), Some(true)) => {
- default_features_msg(name, Some(true), cx);
- }
- // member: default-features = false and
- // workspace: dep = "1.0" should ignore member default-features
- (Some(false), None) => {
- default_features_msg(name, None, cx);
- }
- _ => {}
- }
- d.add_features(self.features.clone());
- d.update_optional(self.optional);
- TomlDependency::Detailed(d)
- }
- }
- })
- }
-}
-
-//. This already has a `Deserialize` impl from version_trim_whitespace
-type MaybeWorkspaceSemverVersion = MaybeWorkspace<semver::Version, TomlWorkspaceField>;
-
-type MaybeWorkspaceString = MaybeWorkspace<String, TomlWorkspaceField>;
-impl<'de> de::Deserialize<'de> for MaybeWorkspaceString {
- fn deserialize<D>(d: D) -> Result<Self, D::Error>
- where
- D: de::Deserializer<'de>,
- {
- struct Visitor;
-
- impl<'de> de::Visitor<'de> for Visitor {
- type Value = MaybeWorkspaceString;
-
- fn expecting(&self, f: &mut fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
- f.write_str("a string or workspace")
- }
-
- fn visit_string<E>(self, value: String) -> Result<Self::Value, E>
- where
- E: de::Error,
- {
- Ok(MaybeWorkspaceString::Defined(value))
- }
-
- fn visit_map<V>(self, map: V) -> Result<Self::Value, V::Error>
- where
- V: de::MapAccess<'de>,
- {
- let mvd = de::value::MapAccessDeserializer::new(map);
- TomlWorkspaceField::deserialize(mvd).map(MaybeWorkspace::Workspace)
- }
- }
-
- d.deserialize_any(Visitor)
- }
-}
-
-type MaybeWorkspaceRustVersion = MaybeWorkspace<RustVersion, TomlWorkspaceField>;
-impl<'de> de::Deserialize<'de> for MaybeWorkspaceRustVersion {
- fn deserialize<D>(d: D) -> Result<Self, D::Error>
- where
- D: de::Deserializer<'de>,
- {
- struct Visitor;
-
- impl<'de> de::Visitor<'de> for Visitor {
- type Value = MaybeWorkspaceRustVersion;
-
- fn expecting(&self, f: &mut fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
- f.write_str("a semver or workspace")
- }
-
- fn visit_string<E>(self, value: String) -> Result<Self::Value, E>
- where
- E: de::Error,
- {
- let value = value.parse::<RustVersion>().map_err(|e| E::custom(e))?;
- Ok(MaybeWorkspaceRustVersion::Defined(value))
- }
-
- fn visit_map<V>(self, map: V) -> Result<Self::Value, V::Error>
- where
- V: de::MapAccess<'de>,
- {
- let mvd = de::value::MapAccessDeserializer::new(map);
- TomlWorkspaceField::deserialize(mvd).map(MaybeWorkspace::Workspace)
- }
- }
-
- d.deserialize_any(Visitor)
- }
-}
-
-type MaybeWorkspaceVecString = MaybeWorkspace<Vec<String>, TomlWorkspaceField>;
-impl<'de> de::Deserialize<'de> for MaybeWorkspaceVecString {
- fn deserialize<D>(d: D) -> Result<Self, D::Error>
- where
- D: de::Deserializer<'de>,
- {
- struct Visitor;
-
- impl<'de> de::Visitor<'de> for Visitor {
- type Value = MaybeWorkspaceVecString;
-
- fn expecting(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
- f.write_str("a vector of strings or workspace")
- }
- fn visit_seq<A>(self, v: A) -> Result<Self::Value, A::Error>
- where
- A: de::SeqAccess<'de>,
- {
- let seq = de::value::SeqAccessDeserializer::new(v);
- Vec::deserialize(seq).map(MaybeWorkspace::Defined)
- }
-
- fn visit_map<V>(self, map: V) -> Result<Self::Value, V::Error>
- where
- V: de::MapAccess<'de>,
- {
- let mvd = de::value::MapAccessDeserializer::new(map);
- TomlWorkspaceField::deserialize(mvd).map(MaybeWorkspace::Workspace)
- }
- }
-
- d.deserialize_any(Visitor)
- }
-}
-
-type MaybeWorkspaceStringOrBool = MaybeWorkspace<StringOrBool, TomlWorkspaceField>;
-impl<'de> de::Deserialize<'de> for MaybeWorkspaceStringOrBool {
- fn deserialize<D>(d: D) -> Result<Self, D::Error>
- where
- D: de::Deserializer<'de>,
- {
- struct Visitor;
-
- impl<'de> de::Visitor<'de> for Visitor {
- type Value = MaybeWorkspaceStringOrBool;
-
- fn expecting(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
- f.write_str("a string, a bool, or workspace")
- }
-
- fn visit_bool<E>(self, v: bool) -> Result<Self::Value, E>
- where
- E: de::Error,
- {
- let b = de::value::BoolDeserializer::new(v);
- StringOrBool::deserialize(b).map(MaybeWorkspace::Defined)
- }
-
- fn visit_string<E>(self, v: String) -> Result<Self::Value, E>
- where
- E: de::Error,
- {
- let string = de::value::StringDeserializer::new(v);
- StringOrBool::deserialize(string).map(MaybeWorkspace::Defined)
- }
-
- fn visit_map<V>(self, map: V) -> Result<Self::Value, V::Error>
- where
- V: de::MapAccess<'de>,
- {
- let mvd = de::value::MapAccessDeserializer::new(map);
- TomlWorkspaceField::deserialize(mvd).map(MaybeWorkspace::Workspace)
- }
- }
-
- d.deserialize_any(Visitor)
- }
-}
-
-type MaybeWorkspaceVecStringOrBool = MaybeWorkspace<VecStringOrBool, TomlWorkspaceField>;
-impl<'de> de::Deserialize<'de> for MaybeWorkspaceVecStringOrBool {
- fn deserialize<D>(d: D) -> Result<Self, D::Error>
- where
- D: de::Deserializer<'de>,
- {
- struct Visitor;
-
- impl<'de> de::Visitor<'de> for Visitor {
- type Value = MaybeWorkspaceVecStringOrBool;
-
- fn expecting(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
- f.write_str("a boolean, a vector of strings, or workspace")
- }
-
- fn visit_bool<E>(self, v: bool) -> Result<Self::Value, E>
- where
- E: de::Error,
- {
- let b = de::value::BoolDeserializer::new(v);
- VecStringOrBool::deserialize(b).map(MaybeWorkspace::Defined)
- }
-
- fn visit_seq<A>(self, v: A) -> Result<Self::Value, A::Error>
- where
- A: de::SeqAccess<'de>,
- {
- let seq = de::value::SeqAccessDeserializer::new(v);
- VecStringOrBool::deserialize(seq).map(MaybeWorkspace::Defined)
- }
-
- fn visit_map<V>(self, map: V) -> Result<Self::Value, V::Error>
- where
- V: de::MapAccess<'de>,
- {
- let mvd = de::value::MapAccessDeserializer::new(map);
- TomlWorkspaceField::deserialize(mvd).map(MaybeWorkspace::Workspace)
- }
- }
-
- d.deserialize_any(Visitor)
- }
-}
-
-type MaybeWorkspaceBtreeMap =
- MaybeWorkspace<BTreeMap<String, BTreeMap<String, String>>, TomlWorkspaceField>;
-
-impl<'de> de::Deserialize<'de> for MaybeWorkspaceBtreeMap {
- fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
- where
- D: de::Deserializer<'de>,
- {
- let value = serde_value::Value::deserialize(deserializer)?;
-
- if let Ok(w) = TomlWorkspaceField::deserialize(
- serde_value::ValueDeserializer::<D::Error>::new(value.clone()),
- ) {
- return if w.workspace() {
- Ok(MaybeWorkspace::Workspace(w))
- } else {
- Err(de::Error::custom("`workspace` cannot be false"))
- };
- }
- BTreeMap::deserialize(serde_value::ValueDeserializer::<D::Error>::new(value))
- .map(MaybeWorkspace::Defined)
- }
-}
-
-#[derive(Deserialize, Serialize, Copy, Clone, Debug)]
-pub struct TomlWorkspaceField {
- #[serde(deserialize_with = "bool_no_false")]
- workspace: bool,
-}
-
-fn bool_no_false<'de, D: de::Deserializer<'de>>(deserializer: D) -> Result<bool, D::Error> {
- let b: bool = Deserialize::deserialize(deserializer)?;
- if b {
- Ok(b)
- } else {
- Err(de::Error::custom("`workspace` cannot be false"))
- }
-}
-
-impl WorkspaceInherit for TomlWorkspaceField {
- fn inherit_toml_table(&self) -> &str {
- "package"
- }
-
- fn workspace(&self) -> bool {
- self.workspace
- }
-}
-
-/// Represents the `package`/`project` sections of a `Cargo.toml`.
-///
-/// Note that the order of the fields matters, since this is the order they
-/// are serialized to a TOML file. For example, you cannot have values after
-/// the field `metadata`, since it is a table and values cannot appear after
-/// tables.
-#[derive(Deserialize, Serialize, Clone, Debug)]
-#[serde(rename_all = "kebab-case")]
-pub struct TomlPackage {
- edition: Option<MaybeWorkspaceString>,
- rust_version: Option<MaybeWorkspaceRustVersion>,
- name: InternedString,
- #[serde(deserialize_with = "version_trim_whitespace")]
- version: MaybeWorkspaceSemverVersion,
- authors: Option<MaybeWorkspaceVecString>,
- build: Option<StringOrBool>,
- metabuild: Option<StringOrVec>,
- default_target: Option<String>,
- forced_target: Option<String>,
- links: Option<String>,
- exclude: Option<MaybeWorkspaceVecString>,
- include: Option<MaybeWorkspaceVecString>,
- publish: Option<MaybeWorkspaceVecStringOrBool>,
- workspace: Option<String>,
- im_a_teapot: Option<bool>,
- autobins: Option<bool>,
- autoexamples: Option<bool>,
- autotests: Option<bool>,
- autobenches: Option<bool>,
- default_run: Option<String>,
-
- // Package metadata.
- description: Option<MaybeWorkspaceString>,
- homepage: Option<MaybeWorkspaceString>,
- documentation: Option<MaybeWorkspaceString>,
- readme: Option<MaybeWorkspaceStringOrBool>,
- keywords: Option<MaybeWorkspaceVecString>,
- categories: Option<MaybeWorkspaceVecString>,
- license: Option<MaybeWorkspaceString>,
- license_file: Option<MaybeWorkspaceString>,
- repository: Option<MaybeWorkspaceString>,
- resolver: Option<String>,
-
- // Provide a helpful error message for a common user error.
- #[serde(rename = "cargo-features", skip_serializing)]
- _invalid_cargo_features: Option<InvalidCargoFeatures>,
-
- // Note that this field must come last due to the way toml serialization
- // works which requires tables to be emitted after all values.
- metadata: Option<toml::Value>,
-}
-
-#[derive(Debug, Deserialize, Serialize, Clone)]
-pub struct TomlWorkspace {
- members: Option<Vec<String>>,
- #[serde(rename = "default-members")]
- default_members: Option<Vec<String>>,
- exclude: Option<Vec<String>>,
- resolver: Option<String>,
-
- // Properties that can be inherited by members.
- package: Option<InheritableFields>,
- dependencies: Option<BTreeMap<String, TomlDependency>>,
- lints: Option<TomlLints>,
-
- // Note that this field must come last due to the way toml serialization
- // works which requires tables to be emitted after all values.
- metadata: Option<toml::Value>,
-}
-
-/// A group of fields that are inheritable by members of the workspace
-#[derive(Clone, Debug, Default, Deserialize, Serialize)]
-pub struct InheritableFields {
- // We use skip here since it will never be present when deserializing
- // and we don't want it present when serializing
- #[serde(skip)]
- dependencies: Option<BTreeMap<String, TomlDependency>>,
- #[serde(skip)]
- lints: Option<TomlLints>,
-
- version: Option<semver::Version>,
- authors: Option<Vec<String>>,
- description: Option<String>,
- homepage: Option<String>,
- documentation: Option<String>,
- readme: Option<StringOrBool>,
- keywords: Option<Vec<String>>,
- categories: Option<Vec<String>>,
- license: Option<String>,
- #[serde(rename = "license-file")]
- license_file: Option<String>,
- repository: Option<String>,
- publish: Option<VecStringOrBool>,
- edition: Option<String>,
- badges: Option<BTreeMap<String, BTreeMap<String, String>>>,
- exclude: Option<Vec<String>>,
- include: Option<Vec<String>>,
- #[serde(rename = "rust-version")]
- rust_version: Option<RustVersion>,
- // We use skip here since it will never be present when deserializing
- // and we don't want it present when serializing
- #[serde(skip)]
- ws_root: PathBuf,
-}
-
-/// Defines simple getter methods for inheritable fields.
-macro_rules! inheritable_field_getter {
- ( $(($key:literal, $field:ident -> $ret:ty),)* ) => (
- $(
- #[doc = concat!("Gets the field `workspace.", $key, "`.")]
- pub fn $field(&self) -> CargoResult<$ret> {
- let Some(val) = &self.$field else {
- bail!("`workspace.{}` was not defined", $key);
- };
- Ok(val.clone())
- }
- )*
- )
-}
-
-impl InheritableFields {
- inheritable_field_getter! {
- // Please keep this list lexicographically ordered.
- ("dependencies", dependencies -> BTreeMap<String, TomlDependency>),
- ("lints", lints -> TomlLints),
- ("package.authors", authors -> Vec<String>),
- ("package.badges", badges -> BTreeMap<String, BTreeMap<String, String>>),
- ("package.categories", categories -> Vec<String>),
- ("package.description", description -> String),
- ("package.documentation", documentation -> String),
- ("package.edition", edition -> String),
- ("package.exclude", exclude -> Vec<String>),
- ("package.homepage", homepage -> String),
- ("package.include", include -> Vec<String>),
- ("package.keywords", keywords -> Vec<String>),
- ("package.license", license -> String),
- ("package.publish", publish -> VecStringOrBool),
- ("package.repository", repository -> String),
- ("package.rust-version", rust_version -> RustVersion),
- ("package.version", version -> semver::Version),
- }
-
- /// Gets a workspace dependency with the `name`.
- pub fn get_dependency(&self, name: &str, package_root: &Path) -> CargoResult<TomlDependency> {
- let Some(deps) = &self.dependencies else {
- bail!("`workspace.dependencies` was not defined");
- };
- let Some(dep) = deps.get(name) else {
- bail!("`dependency.{name}` was not found in `workspace.dependencies`");
- };
- let mut dep = dep.clone();
- if let TomlDependency::Detailed(detailed) = &mut dep {
- detailed.resolve_path(name, self.ws_root(), package_root)?;
- }
- Ok(dep)
- }
-
- /// Gets the field `workspace.package.license-file`.
- pub fn license_file(&self, package_root: &Path) -> CargoResult<String> {
- let Some(license_file) = &self.license_file else {
- bail!("`workspace.package.license-file` was not defined");
- };
- resolve_relative_path("license-file", &self.ws_root, package_root, license_file)
- }
-
- /// Gets the field `workspace.package.readme`.
- pub fn readme(&self, package_root: &Path) -> CargoResult<StringOrBool> {
- let Some(readme) = readme_for_package(self.ws_root.as_path(), self.readme.as_ref()) else {
- bail!("`workspace.package.readme` was not defined");
- };
- resolve_relative_path("readme", &self.ws_root, package_root, &readme)
- .map(StringOrBool::String)
- }
-
- pub fn ws_root(&self) -> &PathBuf {
- &self.ws_root
- }
-
- pub fn update_deps(&mut self, deps: Option<BTreeMap<String, TomlDependency>>) {
- self.dependencies = deps;
- }
-
- pub fn update_lints(&mut self, lints: Option<TomlLints>) {
- self.lints = lints;
- }
-
- pub fn update_ws_path(&mut self, ws_root: PathBuf) {
- self.ws_root = ws_root;
- }
-}
-
-impl TomlPackage {
- pub fn to_package_id(
- &self,
- source_id: SourceId,
- version: semver::Version,
- ) -> CargoResult<PackageId> {
- PackageId::new(self.name, version, source_id)
- }
-}
-
-struct Context<'a, 'b> {
- deps: &'a mut Vec<Dependency>,
- source_id: SourceId,
- nested_paths: &'a mut Vec<PathBuf>,
- config: &'b Config,
- warnings: &'a mut Vec<String>,
- platform: Option<Platform>,
- root: &'a Path,
- features: &'a Features,
-}
-
-impl TomlManifest {
+impl schema::TomlManifest {
/// Prepares the manifest for publishing.
// - Path and git components of dependency specifications are removed.
// - License path is updated to point within the package.
@@ -1610,7 +192,7 @@ impl TomlManifest {
&self,
ws: &Workspace<'_>,
package_root: &Path,
- ) -> CargoResult<TomlManifest> {
+ ) -> CargoResult<schema::TomlManifest> {
let config = ws.config();
let mut package = self
.package
@@ -1648,7 +230,7 @@ impl TomlManifest {
if abs_license_path.strip_prefix(package_root).is_err() {
// This path points outside of the package root. `cargo package`
// will copy it into the root, so adjust the path to this location.
- package.license_file = Some(MaybeWorkspace::Defined(
+ package.license_file = Some(schema::MaybeWorkspace::Defined(
license_path
.file_name()
.unwrap()
@@ -1664,27 +246,29 @@ impl TomlManifest {
.as_defined()
.context("readme should have been resolved before `prepare_for_publish()`")?;
match readme {
- StringOrBool::String(readme) => {
+ schema::StringOrBool::String(readme) => {
let readme_path = Path::new(&readme);
let abs_readme_path = paths::normalize_path(&package_root.join(readme_path));
if abs_readme_path.strip_prefix(package_root).is_err() {
// This path points outside of the package root. `cargo package`
// will copy it into the root, so adjust the path to this location.
- package.readme = Some(MaybeWorkspace::Defined(StringOrBool::String(
- readme_path
- .file_name()
- .unwrap()
- .to_str()
- .unwrap()
- .to_string(),
- )));
+ package.readme = Some(schema::MaybeWorkspace::Defined(
+ schema::StringOrBool::String(
+ readme_path
+ .file_name()
+ .unwrap()
+ .to_str()
+ .unwrap()
+ .to_string(),
+ ),
+ ));
}
}
- StringOrBool::Bool(_) => {}
+ schema::StringOrBool::Bool(_) => {}
}
}
- let all = |_d: &TomlDependency| true;
- return Ok(TomlManifest {
+ let all = |_d: &schema::TomlDependency| true;
+ return Ok(schema::TomlManifest {
package: Some(package),
project: None,
profile: self.profile.clone(),
@@ -1696,19 +280,11 @@ impl TomlManifest {
dependencies: map_deps(config, self.dependencies.as_ref(), all)?,
dev_dependencies: map_deps(
config,
- self.dev_dependencies
- .as_ref()
- .or_else(|| self.dev_dependencies2.as_ref()),
- TomlDependency::is_version_specified,
+ self.dev_dependencies(),
+ schema::TomlDependency::is_version_specified,
)?,
dev_dependencies2: None,
- build_dependencies: map_deps(
- config,
- self.build_dependencies
- .as_ref()
- .or_else(|| self.build_dependencies2.as_ref()),
- all,
- )?,
+ build_dependencies: map_deps(config, self.build_dependencies(), all)?,
build_dependencies2: None,
features: self.features.clone(),
target: match self.target.as_ref().map(|target_map| {
@@ -1717,23 +293,15 @@ impl TomlManifest {
.map(|(k, v)| {
Ok((
k.clone(),
- TomlPlatform {
+ schema::TomlPlatform {
dependencies: map_deps(config, v.dependencies.as_ref(), all)?,
dev_dependencies: map_deps(
config,
- v.dev_dependencies
- .as_ref()
- .or_else(|| v.dev_dependencies2.as_ref()),
- TomlDependency::is_version_specified,
+ v.dev_dependencies(),
+ schema::TomlDependency::is_version_specified,
)?,
dev_dependencies2: None,
- build_dependencies: map_deps(
- config,
- v.build_dependencies
- .as_ref()
- .or_else(|| v.build_dependencies2.as_ref()),
- all,
- )?,
+ build_dependencies: map_deps(config, v.build_dependencies(), all)?,
build_dependencies2: None,
},
))
@@ -1754,14 +322,14 @@ impl TomlManifest {
fn map_deps(
config: &Config,
- deps: Option<&BTreeMap<String, MaybeWorkspaceDependency>>,
- filter: impl Fn(&TomlDependency) -> bool,
- ) -> CargoResult<Option<BTreeMap<String, MaybeWorkspaceDependency>>> {
+ deps: Option<&BTreeMap<String, schema::MaybeWorkspaceDependency>>,
+ filter: impl Fn(&schema::TomlDependency) -> bool,
+ ) -> CargoResult<Option<BTreeMap<String, schema::MaybeWorkspaceDependency>>> {
let Some(deps) = deps else { return Ok(None) };
let deps = deps
.iter()
.filter(|(_k, v)| {
- if let MaybeWorkspace::Defined(def) = v {
+ if let schema::MaybeWorkspace::Defined(def) = v {
filter(def)
} else {
false
@@ -1774,10 +342,10 @@ impl TomlManifest {
fn map_dependency(
config: &Config,
- dep: &MaybeWorkspaceDependency,
- ) -> CargoResult<MaybeWorkspaceDependency> {
+ dep: &schema::MaybeWorkspaceDependency,
+ ) -> CargoResult<schema::MaybeWorkspaceDependency> {
let dep = match dep {
- MaybeWorkspace::Defined(TomlDependency::Detailed(d)) => {
+ schema::MaybeWorkspace::Defined(schema::TomlDependency::Detailed(d)) => {
let mut d = d.clone();
// Path dependencies become crates.io deps.
d.path.take();
@@ -1792,19 +360,21 @@ impl TomlManifest {
}
Ok(d)
}
- MaybeWorkspace::Defined(TomlDependency::Simple(s)) => Ok(DetailedTomlDependency {
- version: Some(s.clone()),
- ..Default::default()
- }),
+ schema::MaybeWorkspace::Defined(schema::TomlDependency::Simple(s)) => {
+ Ok(schema::DetailedTomlDependency {
+ version: Some(s.clone()),
+ ..Default::default()
+ })
+ }
_ => unreachable!(),
};
- dep.map(TomlDependency::Detailed)
- .map(MaybeWorkspace::Defined)
+ dep.map(schema::TomlDependency::Detailed)
+ .map(schema::MaybeWorkspace::Defined)
}
}
pub fn to_real_manifest(
- me: &Rc<TomlManifest>,
+ me: schema::TomlManifest,
embedded: bool,
source_id: SourceId,
package_root: &Path,
@@ -1814,7 +384,7 @@ impl TomlManifest {
config: &Config,
resolved_path: &Path,
workspace_config: &WorkspaceConfig,
- ) -> CargoResult<InheritableFields> {
+ ) -> CargoResult<schema::InheritableFields> {
match workspace_config {
WorkspaceConfig::Root(root) => Ok(root.inheritable().clone()),
WorkspaceConfig::Member {
@@ -1928,25 +498,31 @@ impl TomlManifest {
let resolved_path = package_root.join("Cargo.toml");
- let inherit_cell: LazyCell<InheritableFields> = LazyCell::new();
+ let inherit_cell: LazyCell<schema::InheritableFields> = LazyCell::new();
let inherit =
|| inherit_cell.try_borrow_with(|| get_ws(config, &resolved_path, &workspace_config));
let version = package
.version
.clone()
- .resolve("version", || inherit()?.version())?;
+ .map(|version| version.resolve("version", || inherit()?.version()))
+ .transpose()?;
- package.version = MaybeWorkspace::Defined(version.clone());
+ package.version = version.clone().map(schema::MaybeWorkspace::Defined);
- let pkgid = package.to_package_id(source_id, version)?;
+ let pkgid = package.to_package_id(
+ source_id,
+ version
+ .clone()
+ .unwrap_or_else(|| semver::Version::new(0, 0, 0)),
+ );
let edition = if let Some(edition) = package.edition.clone() {
let edition: Edition = edition
.resolve("edition", || inherit()?.edition())?
.parse()
.with_context(|| "failed to parse the `edition` key")?;
- package.edition = Some(MaybeWorkspace::Defined(edition.to_string()));
+ package.edition = Some(schema::MaybeWorkspace::Defined(edition.to_string()));
edition
} else {
Edition::Edition2015
@@ -1954,10 +530,12 @@ impl TomlManifest {
// Add these lines if start a new unstable edition.
// ```
// if edition == Edition::Edition20xx {
- // features.require(Feature::edition20xx))?;
+ // features.require(Feature::edition20xx())?;
// }
// ```
- if !edition.is_stable() {
+ if edition == Edition::Edition2024 {
+ features.require(Feature::edition2024())?;
+ } else if !edition.is_stable() {
// Guard in case someone forgets to add .require()
return Err(util::errors::internal(format!(
"edition {} should be gated",
@@ -1969,7 +547,7 @@ impl TomlManifest {
let rust_version = rust_version
.clone()
.resolve("rust_version", || inherit()?.rust_version())?;
- let req = rust_version.caret_req();
+ let req = rust_version.to_caret_req();
if let Some(first_version) = edition.first_version() {
let unsupported =
semver::Version::new(first_version.major, first_version.minor - 1, 9999);
@@ -2008,7 +586,7 @@ impl TomlManifest {
// If we have a lib with no path, use the inferred lib or else the package name.
let targets = targets(
&features,
- me,
+ &me,
package_name,
package_root,
edition,
@@ -2068,11 +646,11 @@ impl TomlManifest {
fn process_dependencies(
cx: &mut Context<'_, '_>,
- new_deps: Option<&BTreeMap<String, MaybeWorkspaceDependency>>,
+ new_deps: Option<&BTreeMap<String, schema::MaybeWorkspaceDependency>>,
kind: Option<DepKind>,
workspace_config: &WorkspaceConfig,
- inherit_cell: &LazyCell<InheritableFields>,
- ) -> CargoResult<Option<BTreeMap<String, MaybeWorkspaceDependency>>> {
+ inherit_cell: &LazyCell<schema::InheritableFields>,
+ ) -> CargoResult<Option<BTreeMap<String, schema::MaybeWorkspaceDependency>>> {
let Some(dependencies) = new_deps else {
return Ok(None);
};
@@ -2083,7 +661,7 @@ impl TomlManifest {
})
};
- let mut deps: BTreeMap<String, MaybeWorkspaceDependency> = BTreeMap::new();
+ let mut deps: BTreeMap<String, schema::MaybeWorkspaceDependency> = BTreeMap::new();
for (n, v) in dependencies.iter() {
let resolved = v
.clone()
@@ -2102,7 +680,10 @@ impl TomlManifest {
};
unused_dep_keys(name_in_toml, &table_in_toml, v.unused_keys(), cx.warnings);
cx.deps.push(dep);
- deps.insert(n.to_string(), MaybeWorkspace::Defined(resolved.clone()));
+ deps.insert(
+ n.to_string(),
+ schema::MaybeWorkspace::Defined(resolved.clone()),
+ );
}
Ok(Some(deps))
}
@@ -2118,10 +699,7 @@ impl TomlManifest {
if me.dev_dependencies.is_some() && me.dev_dependencies2.is_some() {
warn_on_deprecated("dev-dependencies", package_name, "package", cx.warnings);
}
- let dev_deps = me
- .dev_dependencies
- .as_ref()
- .or_else(|| me.dev_dependencies2.as_ref());
+ let dev_deps = me.dev_dependencies();
let dev_deps = process_dependencies(
&mut cx,
dev_deps,
@@ -2132,10 +710,7 @@ impl TomlManifest {
if me.build_dependencies.is_some() && me.build_dependencies2.is_some() {
warn_on_deprecated("build-dependencies", package_name, "package", cx.warnings);
}
- let build_deps = me
- .build_dependencies
- .as_ref()
- .or_else(|| me.build_dependencies2.as_ref());
+ let build_deps = me.build_dependencies();
let build_deps = process_dependencies(
&mut cx,
build_deps,
@@ -2150,10 +725,10 @@ impl TomlManifest {
.map(|mw| mw.resolve(|| inherit()?.lints()))
.transpose()?;
let lints = verify_lints(lints)?;
- let default = TomlLints::default();
+ let default = schema::TomlLints::default();
let rustflags = lints_to_rustflags(lints.as_ref().unwrap_or(&default));
- let mut target: BTreeMap<String, TomlPlatform> = BTreeMap::new();
+ let mut target: BTreeMap<String, schema::TomlPlatform> = BTreeMap::new();
for (name, platform) in me.target.iter().flatten() {
cx.platform = {
let platform: Platform = name.parse()?;
@@ -2170,10 +745,7 @@ impl TomlManifest {
if platform.build_dependencies.is_some() && platform.build_dependencies2.is_some() {
warn_on_deprecated("build-dependencies", name, "platform target", cx.warnings);
}
- let build_deps = platform
- .build_dependencies
- .as_ref()
- .or_else(|| platform.build_dependencies2.as_ref());
+ let build_deps = platform.build_dependencies();
let build_deps = process_dependencies(
&mut cx,
build_deps,
@@ -2184,10 +756,7 @@ impl TomlManifest {
if platform.dev_dependencies.is_some() && platform.dev_dependencies2.is_some() {
warn_on_deprecated("dev-dependencies", name, "platform target", cx.warnings);
}
- let dev_deps = platform
- .dev_dependencies
- .as_ref()
- .or_else(|| platform.dev_dependencies2.as_ref());
+ let dev_deps = platform.dev_dependencies();
let dev_deps = process_dependencies(
&mut cx,
dev_deps,
@@ -2197,7 +766,7 @@ impl TomlManifest {
)?;
target.insert(
name.clone(),
- TomlPlatform {
+ schema::TomlPlatform {
dependencies: deps,
build_dependencies: build_deps,
build_dependencies2: None,
@@ -2248,7 +817,17 @@ impl TomlManifest {
let summary = Summary::new(
pkgid,
deps,
- me.features.as_ref().unwrap_or(&empty_features),
+ &me.features
+ .as_ref()
+ .unwrap_or(&empty_features)
+ .iter()
+ .map(|(k, v)| {
+ (
+ InternedString::new(k),
+ v.iter().map(InternedString::from).collect(),
+ )
+ })
+ .collect(),
package.links.as_deref(),
rust_version.clone(),
)?;
@@ -2326,52 +905,54 @@ impl TomlManifest {
package.description = metadata
.description
.clone()
- .map(|description| MaybeWorkspace::Defined(description));
+ .map(|description| schema::MaybeWorkspace::Defined(description));
package.homepage = metadata
.homepage
.clone()
- .map(|homepage| MaybeWorkspace::Defined(homepage));
+ .map(|homepage| schema::MaybeWorkspace::Defined(homepage));
package.documentation = metadata
.documentation
.clone()
- .map(|documentation| MaybeWorkspace::Defined(documentation));
+ .map(|documentation| schema::MaybeWorkspace::Defined(documentation));
package.readme = metadata
.readme
.clone()
- .map(|readme| MaybeWorkspace::Defined(StringOrBool::String(readme)));
+ .map(|readme| schema::MaybeWorkspace::Defined(schema::StringOrBool::String(readme)));
package.authors = package
.authors
.as_ref()
- .map(|_| MaybeWorkspace::Defined(metadata.authors.clone()));
+ .map(|_| schema::MaybeWorkspace::Defined(metadata.authors.clone()));
package.license = metadata
.license
.clone()
- .map(|license| MaybeWorkspace::Defined(license));
+ .map(|license| schema::MaybeWorkspace::Defined(license));
package.license_file = metadata
.license_file
.clone()
- .map(|license_file| MaybeWorkspace::Defined(license_file));
+ .map(|license_file| schema::MaybeWorkspace::Defined(license_file));
package.repository = metadata
.repository
.clone()
- .map(|repository| MaybeWorkspace::Defined(repository));
+ .map(|repository| schema::MaybeWorkspace::Defined(repository));
package.keywords = package
.keywords
.as_ref()
- .map(|_| MaybeWorkspace::Defined(metadata.keywords.clone()));
+ .map(|_| schema::MaybeWorkspace::Defined(metadata.keywords.clone()));
package.categories = package
.categories
.as_ref()
- .map(|_| MaybeWorkspace::Defined(metadata.categories.clone()));
- package.rust_version = rust_version.clone().map(|rv| MaybeWorkspace::Defined(rv));
+ .map(|_| schema::MaybeWorkspace::Defined(metadata.categories.clone()));
+ package.rust_version = rust_version
+ .clone()
+ .map(|rv| schema::MaybeWorkspace::Defined(rv));
package.exclude = package
.exclude
.as_ref()
- .map(|_| MaybeWorkspace::Defined(exclude.clone()));
+ .map(|_| schema::MaybeWorkspace::Defined(exclude.clone()));
package.include = package
.include
.as_ref()
- .map(|_| MaybeWorkspace::Defined(include.clone()));
+ .map(|_| schema::MaybeWorkspace::Defined(include.clone()));
let profiles = me.profile.clone();
if let Some(profiles) = &profiles {
@@ -2384,14 +965,19 @@ impl TomlManifest {
.clone()
.map(|publish| publish.resolve("publish", || inherit()?.publish()).unwrap());
- package.publish = publish.clone().map(|p| MaybeWorkspace::Defined(p));
+ package.publish = publish.clone().map(|p| schema::MaybeWorkspace::Defined(p));
let publish = match publish {
- Some(VecStringOrBool::VecString(ref vecstring)) => Some(vecstring.clone()),
- Some(VecStringOrBool::Bool(false)) => Some(vec![]),
- None | Some(VecStringOrBool::Bool(true)) => None,
+ Some(schema::VecStringOrBool::VecString(ref vecstring)) => Some(vecstring.clone()),
+ Some(schema::VecStringOrBool::Bool(false)) => Some(vec![]),
+ Some(schema::VecStringOrBool::Bool(true)) => None,
+ None => version.is_none().then_some(vec![]),
};
+ if version.is_none() && publish != Some(vec![]) {
+ bail!("`package.publish` requires `package.version` be specified");
+ }
+
if summary.features().contains_key("default-features") {
warnings.push(
"`default-features = [\"..\"]` was found in [features]. \
@@ -2425,7 +1011,7 @@ impl TomlManifest {
.transpose()?
.map(CompileKind::Target);
let custom_metadata = package.metadata.clone();
- let resolved_toml = TomlManifest {
+ let resolved_toml = schema::TomlManifest {
cargo_features: me.cargo_features.clone(),
package: Some(package.clone()),
project: None,
@@ -2448,8 +1034,8 @@ impl TomlManifest {
badges: me
.badges
.as_ref()
- .map(|_| MaybeWorkspace::Defined(metadata.badges.clone())),
- lints: lints.map(|lints| MaybeWorkspaceLints {
+ .map(|_| schema::MaybeWorkspace::Defined(metadata.badges.clone())),
+ lints: lints.map(|lints| schema::MaybeWorkspaceLints {
workspace: false,
lints,
}),
@@ -2504,7 +1090,7 @@ impl TomlManifest {
}
fn to_virtual_manifest(
- me: &Rc<TomlManifest>,
+ me: schema::TomlManifest,
source_id: SourceId,
root: &Path,
config: &Config,
@@ -2533,10 +1119,10 @@ impl TomlManifest {
if me.dependencies.is_some() {
bail!("this virtual manifest specifies a [dependencies] section, which is not allowed");
}
- if me.dev_dependencies.is_some() || me.dev_dependencies2.is_some() {
+ if me.dev_dependencies().is_some() {
bail!("this virtual manifest specifies a [dev-dependencies] section, which is not allowed");
}
- if me.build_dependencies.is_some() || me.build_dependencies2.is_some() {
+ if me.build_dependencies().is_some() {
bail!("this virtual manifest specifies a [build-dependencies] section, which is not allowed");
}
if me.features.is_some() {
@@ -2643,7 +1229,7 @@ impl TomlManifest {
);
}
- let mut dep = replacement.to_dependency(spec.name().as_str(), cx, None)?;
+ let mut dep = replacement.to_dependency(spec.name(), cx, None)?;
let version = spec.version().ok_or_else(|| {
anyhow!(
"replacements must specify a version \
@@ -2657,8 +1243,7 @@ impl TomlManifest {
replacement.unused_keys(),
&mut cx.warnings,
);
- dep.set_version_req(OptVersionReq::exact(&version))
- .lock_version(&version);
+ dep.set_version_req(OptVersionReq::exact(&version));
replace.push((spec, dep));
}
Ok(replace)
@@ -2697,41 +1282,20 @@ impl TomlManifest {
}
Ok(patch)
}
+}
- /// Returns the path to the build script if one exists for this crate.
- fn maybe_custom_build(
- &self,
- build: &Option<StringOrBool>,
- package_root: &Path,
- ) -> Option<PathBuf> {
- let build_rs = package_root.join("build.rs");
- match *build {
- // Explicitly no build script.
- Some(StringOrBool::Bool(false)) => None,
- Some(StringOrBool::Bool(true)) => Some(build_rs),
- Some(StringOrBool::String(ref s)) => Some(PathBuf::from(s)),
- None => {
- // If there is a `build.rs` file next to the `Cargo.toml`, assume it is
- // a build script.
- if build_rs.is_file() {
- Some(build_rs)
- } else {
- None
- }
- }
- }
- }
-
- pub fn has_profiles(&self) -> bool {
- self.profile.is_some()
- }
-
- pub fn features(&self) -> Option<&BTreeMap<InternedString, Vec<InternedString>>> {
- self.features.as_ref()
- }
+struct Context<'a, 'b> {
+ deps: &'a mut Vec<Dependency>,
+ source_id: SourceId,
+ nested_paths: &'a mut Vec<PathBuf>,
+ config: &'b Config,
+ warnings: &'a mut Vec<String>,
+ platform: Option<Platform>,
+ root: &'a Path,
+ features: &'a Features,
}
-fn verify_lints(lints: Option<TomlLints>) -> CargoResult<Option<TomlLints>> {
+fn verify_lints(lints: Option<schema::TomlLints>) -> CargoResult<Option<schema::TomlLints>> {
let Some(lints) = lints else {
return Ok(None);
};
@@ -2762,7 +1326,7 @@ fn verify_lints(lints: Option<TomlLints>) -> CargoResult<Option<TomlLints>> {
Ok(Some(lints))
}
-fn lints_to_rustflags(lints: &TomlLints) -> Vec<String> {
+fn lints_to_rustflags(lints: &schema::TomlLints) -> Vec<String> {
let mut rustflags = lints
.iter()
.flat_map(|(tool, lints)| {
@@ -2802,7 +1366,7 @@ fn unused_dep_keys(
fn inheritable_from_path(
config: &Config,
workspace_path: PathBuf,
-) -> CargoResult<InheritableFields> {
+) -> CargoResult<schema::InheritableFields> {
// Workspace path should have Cargo.toml at the end
let workspace_path_root = workspace_path.parent().unwrap();
@@ -2829,14 +1393,17 @@ fn inheritable_from_path(
}
}
-/// Returns the name of the README file for a [`TomlPackage`].
-pub fn readme_for_package(package_root: &Path, readme: Option<&StringOrBool>) -> Option<String> {
+/// Returns the name of the README file for a [`schema::TomlPackage`].
+fn readme_for_package(
+ package_root: &Path,
+ readme: Option<&schema::StringOrBool>,
+) -> Option<String> {
match &readme {
None => default_readme_from_package_root(package_root),
Some(value) => match value {
- StringOrBool::Bool(false) => None,
- StringOrBool::Bool(true) => Some("README.md".to_string()),
- StringOrBool::String(v) => Some(v.clone()),
+ schema::StringOrBool::Bool(false) => None,
+ schema::StringOrBool::Bool(true) => Some("README.md".to_string()),
+ schema::StringOrBool::String(v) => Some(v.clone()),
},
}
}
@@ -2881,7 +1448,254 @@ fn unique_build_targets(
Ok(())
}
-impl<P: ResolveToPath + Clone> TomlDependency<P> {
+/// Defines simple getter methods for inheritable fields.
+macro_rules! inheritable_field_getter {
+ ( $(($key:literal, $field:ident -> $ret:ty),)* ) => (
+ $(
+ #[doc = concat!("Gets the field `workspace.", $key, "`.")]
+ fn $field(&self) -> CargoResult<$ret> {
+ let Some(val) = &self.$field else {
+ bail!("`workspace.{}` was not defined", $key);
+ };
+ Ok(val.clone())
+ }
+ )*
+ )
+}
+
+impl schema::InheritableFields {
+ inheritable_field_getter! {
+ // Please keep this list lexicographically ordered.
+ ("lints", lints -> schema::TomlLints),
+ ("package.authors", authors -> Vec<String>),
+ ("package.badges", badges -> BTreeMap<String, BTreeMap<String, String>>),
+ ("package.categories", categories -> Vec<String>),
+ ("package.description", description -> String),
+ ("package.documentation", documentation -> String),
+ ("package.edition", edition -> String),
+ ("package.exclude", exclude -> Vec<String>),
+ ("package.homepage", homepage -> String),
+ ("package.include", include -> Vec<String>),
+ ("package.keywords", keywords -> Vec<String>),
+ ("package.license", license -> String),
+ ("package.publish", publish -> schema::VecStringOrBool),
+ ("package.repository", repository -> String),
+ ("package.rust-version", rust_version -> RustVersion),
+ ("package.version", version -> semver::Version),
+ }
+
+ /// Gets a workspace dependency with the `name`.
+ fn get_dependency(
+ &self,
+ name: &str,
+ package_root: &Path,
+ ) -> CargoResult<schema::TomlDependency> {
+ let Some(deps) = &self.dependencies else {
+ bail!("`workspace.dependencies` was not defined");
+ };
+ let Some(dep) = deps.get(name) else {
+ bail!("`dependency.{name}` was not found in `workspace.dependencies`");
+ };
+ let mut dep = dep.clone();
+ if let schema::TomlDependency::Detailed(detailed) = &mut dep {
+ detailed.resolve_path(name, self.ws_root(), package_root)?;
+ }
+ Ok(dep)
+ }
+
+ /// Gets the field `workspace.package.license-file`.
+ fn license_file(&self, package_root: &Path) -> CargoResult<String> {
+ let Some(license_file) = &self.license_file else {
+ bail!("`workspace.package.license-file` was not defined");
+ };
+ resolve_relative_path("license-file", &self.ws_root, package_root, license_file)
+ }
+
+ /// Gets the field `workspace.package.readme`.
+ fn readme(&self, package_root: &Path) -> CargoResult<schema::StringOrBool> {
+ let Some(readme) = readme_for_package(self.ws_root.as_path(), self.readme.as_ref()) else {
+ bail!("`workspace.package.readme` was not defined");
+ };
+ resolve_relative_path("readme", &self.ws_root, package_root, &readme)
+ .map(schema::StringOrBool::String)
+ }
+
+ fn ws_root(&self) -> &PathBuf {
+ &self.ws_root
+ }
+
+ fn update_deps(&mut self, deps: Option<BTreeMap<String, schema::TomlDependency>>) {
+ self.dependencies = deps;
+ }
+
+ fn update_lints(&mut self, lints: Option<schema::TomlLints>) {
+ self.lints = lints;
+ }
+
+ fn update_ws_path(&mut self, ws_root: PathBuf) {
+ self.ws_root = ws_root;
+ }
+}
+
+impl schema::TomlPackage {
+ fn to_package_id(&self, source_id: SourceId, version: semver::Version) -> PackageId {
+ PackageId::pure(self.name.as_str().into(), version, source_id)
+ }
+}
+
+/// This Trait exists to make [`schema::MaybeWorkspace::Workspace`] generic. It makes deserialization of
+/// [`schema::MaybeWorkspace`] much easier, as well as making error messages for
+/// [`schema::MaybeWorkspace::resolve`] much nicer
+///
+/// Implementors should have a field `workspace` with the type of `bool`. It is used to ensure
+/// `workspace` is not `false` in a `Cargo.toml`
+pub trait WorkspaceInherit {
+ /// This is the workspace table that is being inherited from.
+ /// For example `[workspace.dependencies]` would be the table "dependencies"
+ fn inherit_toml_table(&self) -> &str;
+
+ /// This is used to output the value of the implementors `workspace` field
+ fn workspace(&self) -> bool;
+}
+
+impl<T, W: WorkspaceInherit> schema::MaybeWorkspace<T, W> {
+ fn resolve<'a>(
+ self,
+ label: &str,
+ get_ws_inheritable: impl FnOnce() -> CargoResult<T>,
+ ) -> CargoResult<T> {
+ match self {
+ schema::MaybeWorkspace::Defined(value) => Ok(value),
+ schema::MaybeWorkspace::Workspace(w) => get_ws_inheritable().with_context(|| {
+ format!(
+ "error inheriting `{label}` from workspace root manifest's `workspace.{}.{label}`",
+ w.inherit_toml_table(),
+ )
+ }),
+ }
+ }
+
+ fn resolve_with_self<'a>(
+ self,
+ label: &str,
+ get_ws_inheritable: impl FnOnce(&W) -> CargoResult<T>,
+ ) -> CargoResult<T> {
+ match self {
+ schema::MaybeWorkspace::Defined(value) => Ok(value),
+ schema::MaybeWorkspace::Workspace(w) => get_ws_inheritable(&w).with_context(|| {
+ format!(
+ "error inheriting `{label}` from workspace root manifest's `workspace.{}.{label}`",
+ w.inherit_toml_table(),
+ )
+ }),
+ }
+ }
+
+ fn as_defined(&self) -> Option<&T> {
+ match self {
+ schema::MaybeWorkspace::Workspace(_) => None,
+ schema::MaybeWorkspace::Defined(defined) => Some(defined),
+ }
+ }
+}
+
+impl WorkspaceInherit for schema::TomlWorkspaceField {
+ fn inherit_toml_table(&self) -> &str {
+ "package"
+ }
+
+ fn workspace(&self) -> bool {
+ self.workspace
+ }
+}
+
+impl schema::TomlWorkspaceDependency {
+ fn resolve<'a>(
+ &self,
+ name: &str,
+ inheritable: impl FnOnce() -> CargoResult<&'a schema::InheritableFields>,
+ cx: &mut Context<'_, '_>,
+ ) -> CargoResult<schema::TomlDependency> {
+ fn default_features_msg(label: &str, ws_def_feat: Option<bool>, cx: &mut Context<'_, '_>) {
+ let ws_def_feat = match ws_def_feat {
+ Some(true) => "true",
+ Some(false) => "false",
+ None => "not specified",
+ };
+ cx.warnings.push(format!(
+ "`default-features` is ignored for {label}, since `default-features` was \
+ {ws_def_feat} for `workspace.dependencies.{label}`, \
+ this could become a hard error in the future"
+ ))
+ }
+ if self.default_features.is_some() && self.default_features2.is_some() {
+ warn_on_deprecated("default-features", name, "dependency", cx.warnings);
+ }
+ inheritable()?.get_dependency(name, cx.root).map(|d| {
+ match d {
+ schema::TomlDependency::Simple(s) => {
+ if let Some(false) = self.default_features() {
+ default_features_msg(name, None, cx);
+ }
+ if self.optional.is_some() || self.features.is_some() || self.public.is_some() {
+ schema::TomlDependency::Detailed(schema::DetailedTomlDependency {
+ version: Some(s),
+ optional: self.optional,
+ features: self.features.clone(),
+ public: self.public,
+ ..Default::default()
+ })
+ } else {
+ schema::TomlDependency::Simple(s)
+ }
+ }
+ schema::TomlDependency::Detailed(d) => {
+ let mut d = d.clone();
+ match (self.default_features(), d.default_features()) {
+ // member: default-features = true and
+ // workspace: default-features = false should turn on
+ // default-features
+ (Some(true), Some(false)) => {
+ d.default_features = Some(true);
+ }
+ // member: default-features = false and
+ // workspace: default-features = true should ignore member
+ // default-features
+ (Some(false), Some(true)) => {
+ default_features_msg(name, Some(true), cx);
+ }
+ // member: default-features = false and
+ // workspace: dep = "1.0" should ignore member default-features
+ (Some(false), None) => {
+ default_features_msg(name, None, cx);
+ }
+ _ => {}
+ }
+ // Inherit the workspace configuration for `public` unless
+ // it's explicitly specified for this dependency.
+ if let Some(public) = self.public {
+ d.public = Some(public);
+ }
+ d.add_features(self.features.clone());
+ d.update_optional(self.optional);
+ schema::TomlDependency::Detailed(d)
+ }
+ }
+ })
+ }
+}
+
+impl WorkspaceInherit for schema::TomlWorkspaceDependency {
+ fn inherit_toml_table(&self) -> &str {
+ "dependencies"
+ }
+
+ fn workspace(&self) -> bool {
+ self.workspace
+ }
+}
+
+impl<P: ResolveToPath + Clone> schema::TomlDependency<P> {
pub(crate) fn to_dependency_split(
&self,
name: &str,
@@ -2917,31 +1731,54 @@ impl<P: ResolveToPath + Clone> TomlDependency<P> {
kind: Option<DepKind>,
) -> CargoResult<Dependency> {
match *self {
- TomlDependency::Simple(ref version) => DetailedTomlDependency::<P> {
+ schema::TomlDependency::Simple(ref version) => schema::DetailedTomlDependency::<P> {
version: Some(version.clone()),
..Default::default()
}
.to_dependency(name, cx, kind),
- TomlDependency::Detailed(ref details) => details.to_dependency(name, cx, kind),
+ schema::TomlDependency::Detailed(ref details) => details.to_dependency(name, cx, kind),
}
}
+}
- fn is_version_specified(&self) -> bool {
- match self {
- TomlDependency::Detailed(d) => d.version.is_some(),
- TomlDependency::Simple(..) => true,
- }
+impl schema::DetailedTomlDependency {
+ fn add_features(&mut self, features: Option<Vec<String>>) {
+ self.features = match (self.features.clone(), features.clone()) {
+ (Some(dep_feat), Some(inherit_feat)) => Some(
+ dep_feat
+ .into_iter()
+ .chain(inherit_feat)
+ .collect::<Vec<String>>(),
+ ),
+ (Some(dep_fet), None) => Some(dep_fet),
+ (None, Some(inherit_feat)) => Some(inherit_feat),
+ (None, None) => None,
+ };
}
- fn is_optional(&self) -> bool {
- match self {
- TomlDependency::Detailed(d) => d.optional.unwrap_or(false),
- TomlDependency::Simple(..) => false,
+ fn update_optional(&mut self, optional: Option<bool>) {
+ self.optional = optional;
+ }
+
+ fn resolve_path(
+ &mut self,
+ name: &str,
+ root_path: &Path,
+ package_root: &Path,
+ ) -> CargoResult<()> {
+ if let Some(rel_path) = &self.path {
+ self.path = Some(resolve_relative_path(
+ name,
+ root_path,
+ package_root,
+ rel_path,
+ )?)
}
+ Ok(())
}
}
-impl<P: ResolveToPath + Clone> DetailedTomlDependency<P> {
+impl<P: ResolveToPath + Clone> schema::DetailedTomlDependency<P> {
fn to_dependency(
&self,
name_in_toml: &str,
@@ -3114,11 +1951,7 @@ impl<P: ResolveToPath + Clone> DetailedTomlDependency<P> {
warn_on_deprecated("default-features", name_in_toml, "dependency", cx.warnings);
}
dep.set_features(self.features.iter().flatten())
- .set_default_features(
- self.default_features
- .or(self.default_features2)
- .unwrap_or(true),
- )
+ .set_default_features(self.default_features().unwrap_or(true))
.set_optional(self.optional.unwrap_or(false))
.set_platform(cx.platform.clone());
if let Some(registry) = &self.registry {
@@ -3186,184 +2019,271 @@ impl<P: ResolveToPath + Clone> DetailedTomlDependency<P> {
}
}
-impl DetailedTomlDependency {
- fn add_features(&mut self, features: Option<Vec<String>>) {
- self.features = match (self.features.clone(), features.clone()) {
- (Some(dep_feat), Some(inherit_feat)) => Some(
- dep_feat
- .into_iter()
- .chain(inherit_feat)
- .collect::<Vec<String>>(),
- ),
- (Some(dep_fet), None) => Some(dep_fet),
- (None, Some(inherit_feat)) => Some(inherit_feat),
- (None, None) => None,
- };
- }
-
- fn update_optional(&mut self, optional: Option<bool>) {
- self.optional = optional;
+impl schema::TomlProfiles {
+ /// Checks syntax validity and unstable feature gate for each profile.
+ ///
+ /// It's a bit unfortunate both `-Z` flags and `cargo-features` are required,
+ /// because profiles can now be set in either `Cargo.toml` or `config.toml`.
+ fn validate(
+ &self,
+ cli_unstable: &CliUnstable,
+ features: &Features,
+ warnings: &mut Vec<String>,
+ ) -> CargoResult<()> {
+ for (name, profile) in &self.0 {
+ profile.validate(name, cli_unstable, features, warnings)?;
+ }
+ Ok(())
}
+}
- fn resolve_path(
- &mut self,
+impl schema::TomlProfile {
+ /// Checks stytax validity and unstable feature gate for a given profile.
+ pub fn validate(
+ &self,
name: &str,
- root_path: &Path,
- package_root: &Path,
+ cli_unstable: &CliUnstable,
+ features: &Features,
+ warnings: &mut Vec<String>,
) -> CargoResult<()> {
- if let Some(rel_path) = &self.path {
- self.path = Some(resolve_relative_path(
+ self.validate_profile(name, cli_unstable, features)?;
+ if let Some(ref profile) = self.build_override {
+ profile.validate_override("build-override")?;
+ profile.validate_profile(&format!("{name}.build-override"), cli_unstable, features)?;
+ }
+ if let Some(ref packages) = self.package {
+ for (override_name, profile) in packages {
+ profile.validate_override("package")?;
+ profile.validate_profile(
+ &format!("{name}.package.{override_name}"),
+ cli_unstable,
+ features,
+ )?;
+ }
+ }
+
+ // Profile name validation
+ restricted_names::validate_profile_name(name)?;
+
+ if let Some(dir_name) = &self.dir_name {
+ // This is disabled for now, as we would like to stabilize named
+ // profiles without this, and then decide in the future if it is
+ // needed. This helps simplify the UI a little.
+ bail!(
+ "dir-name=\"{}\" in profile `{}` is not currently allowed, \
+ directory names are tied to the profile name for custom profiles",
+ dir_name,
+ name
+ );
+ }
+
+ // `inherits` validation
+ if matches!(self.inherits.as_deref(), Some("debug")) {
+ bail!(
+ "profile.{}.inherits=\"debug\" should be profile.{}.inherits=\"dev\"",
name,
- root_path,
- package_root,
- rel_path,
- )?)
+ name
+ );
}
- Ok(())
- }
-}
-#[derive(Default, Serialize, Deserialize, Debug, Clone)]
-#[serde(rename_all = "kebab-case")]
-struct TomlTarget {
- name: Option<String>,
-
- // The intention was to only accept `crate-type` here but historical
- // versions of Cargo also accepted `crate_type`, so look for both.
- crate_type: Option<Vec<String>>,
- #[serde(rename = "crate_type")]
- crate_type2: Option<Vec<String>>,
-
- path: Option<PathValue>,
- // Note that `filename` is used for the cargo-feature `different_binary_name`
- filename: Option<String>,
- test: Option<bool>,
- doctest: Option<bool>,
- bench: Option<bool>,
- doc: Option<bool>,
- plugin: Option<bool>,
- doc_scrape_examples: Option<bool>,
- #[serde(rename = "proc-macro")]
- proc_macro_raw: Option<bool>,
- #[serde(rename = "proc_macro")]
- proc_macro_raw2: Option<bool>,
- harness: Option<bool>,
- required_features: Option<Vec<String>>,
- edition: Option<String>,
-}
+ match name {
+ "doc" => {
+ warnings.push("profile `doc` is deprecated and has no effect".to_string());
+ }
+ "test" | "bench" => {
+ if self.panic.is_some() {
+ warnings.push(format!("`panic` setting is ignored for `{}` profile", name))
+ }
+ }
+ _ => {}
+ }
-#[derive(Clone)]
-struct PathValue(PathBuf);
+ if let Some(panic) = &self.panic {
+ if panic != "unwind" && panic != "abort" {
+ bail!(
+ "`panic` setting of `{}` is not a valid setting, \
+ must be `unwind` or `abort`",
+ panic
+ );
+ }
+ }
-impl<'de> de::Deserialize<'de> for PathValue {
- fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
- where
- D: de::Deserializer<'de>,
- {
- Ok(PathValue(String::deserialize(deserializer)?.into()))
- }
-}
+ if let Some(schema::StringOrBool::String(arg)) = &self.lto {
+ if arg == "true" || arg == "false" {
+ bail!(
+ "`lto` setting of string `\"{arg}\"` for `{name}` profile is not \
+ a valid setting, must be a boolean (`true`/`false`) or a string \
+ (`\"thin\"`/`\"fat\"`/`\"off\"`) or omitted.",
+ );
+ }
+ }
-impl ser::Serialize for PathValue {
- fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
- where
- S: ser::Serializer,
- {
- self.0.serialize(serializer)
+ Ok(())
}
-}
-/// Corresponds to a `target` entry, but `TomlTarget` is already used.
-#[derive(Serialize, Deserialize, Debug, Clone)]
-struct TomlPlatform {
- dependencies: Option<BTreeMap<String, MaybeWorkspaceDependency>>,
- #[serde(rename = "build-dependencies")]
- build_dependencies: Option<BTreeMap<String, MaybeWorkspaceDependency>>,
- #[serde(rename = "build_dependencies")]
- build_dependencies2: Option<BTreeMap<String, MaybeWorkspaceDependency>>,
- #[serde(rename = "dev-dependencies")]
- dev_dependencies: Option<BTreeMap<String, MaybeWorkspaceDependency>>,
- #[serde(rename = "dev_dependencies")]
- dev_dependencies2: Option<BTreeMap<String, MaybeWorkspaceDependency>>,
-}
+ /// Validates a profile.
+ ///
+ /// This is a shallow check, which is reused for the profile itself and any overrides.
+ fn validate_profile(
+ &self,
+ name: &str,
+ cli_unstable: &CliUnstable,
+ features: &Features,
+ ) -> CargoResult<()> {
+ if let Some(codegen_backend) = &self.codegen_backend {
+ match (
+ features.require(Feature::codegen_backend()),
+ cli_unstable.codegen_backend,
+ ) {
+ (Err(e), false) => return Err(e),
+ _ => {}
+ }
-impl TomlTarget {
- fn new() -> TomlTarget {
- TomlTarget::default()
+ if codegen_backend.contains(|c: char| !c.is_ascii_alphanumeric() && c != '_') {
+ bail!(
+ "`profile.{}.codegen-backend` setting of `{}` is not a valid backend name.",
+ name,
+ codegen_backend,
+ );
+ }
+ }
+ if self.rustflags.is_some() {
+ match (
+ features.require(Feature::profile_rustflags()),
+ cli_unstable.profile_rustflags,
+ ) {
+ (Err(e), false) => return Err(e),
+ _ => {}
+ }
+ }
+ if self.trim_paths.is_some() {
+ match (
+ features.require(Feature::trim_paths()),
+ cli_unstable.trim_paths,
+ ) {
+ (Err(e), false) => return Err(e),
+ _ => {}
+ }
+ }
+ Ok(())
}
- fn name(&self) -> String {
- match self.name {
- Some(ref name) => name.clone(),
- None => panic!("target name is required"),
+ /// Validation that is specific to an override.
+ fn validate_override(&self, which: &str) -> CargoResult<()> {
+ if self.package.is_some() {
+ bail!("package-specific profiles cannot be nested");
+ }
+ if self.build_override.is_some() {
+ bail!("build-override profiles cannot be nested");
}
+ if self.panic.is_some() {
+ bail!("`panic` may not be specified in a `{}` profile", which)
+ }
+ if self.lto.is_some() {
+ bail!("`lto` may not be specified in a `{}` profile", which)
+ }
+ if self.rpath.is_some() {
+ bail!("`rpath` may not be specified in a `{}` profile", which)
+ }
+ Ok(())
}
- fn validate_proc_macro(&self, warnings: &mut Vec<String>) {
- if self.proc_macro_raw.is_some() && self.proc_macro_raw2.is_some() {
- warn_on_deprecated(
- "proc-macro",
- self.name().as_str(),
- "library target",
- warnings,
- );
+ /// Overwrite self's values with the given profile.
+ pub fn merge(&mut self, profile: &schema::TomlProfile) {
+ if let Some(v) = &profile.opt_level {
+ self.opt_level = Some(v.clone());
}
- }
- fn proc_macro(&self) -> Option<bool> {
- self.proc_macro_raw.or(self.proc_macro_raw2).or_else(|| {
- if let Some(types) = self.crate_types() {
- if types.contains(&"proc-macro".to_string()) {
- return Some(true);
+ if let Some(v) = &profile.lto {
+ self.lto = Some(v.clone());
+ }
+
+ if let Some(v) = &profile.codegen_backend {
+ self.codegen_backend = Some(v.clone());
+ }
+
+ if let Some(v) = profile.codegen_units {
+ self.codegen_units = Some(v);
+ }
+
+ if let Some(v) = profile.debug {
+ self.debug = Some(v);
+ }
+
+ if let Some(v) = profile.debug_assertions {
+ self.debug_assertions = Some(v);
+ }
+
+ if let Some(v) = &profile.split_debuginfo {
+ self.split_debuginfo = Some(v.clone());
+ }
+
+ if let Some(v) = profile.rpath {
+ self.rpath = Some(v);
+ }
+
+ if let Some(v) = &profile.panic {
+ self.panic = Some(v.clone());
+ }
+
+ if let Some(v) = profile.overflow_checks {
+ self.overflow_checks = Some(v);
+ }
+
+ if let Some(v) = profile.incremental {
+ self.incremental = Some(v);
+ }
+
+ if let Some(v) = &profile.rustflags {
+ self.rustflags = Some(v.clone());
+ }
+
+ if let Some(other_package) = &profile.package {
+ match &mut self.package {
+ Some(self_package) => {
+ for (spec, other_pkg_profile) in other_package {
+ match self_package.get_mut(spec) {
+ Some(p) => p.merge(other_pkg_profile),
+ None => {
+ self_package.insert(spec.clone(), other_pkg_profile.clone());
+ }
+ }
+ }
}
+ None => self.package = Some(other_package.clone()),
}
- None
- })
- }
+ }
- fn validate_crate_types(&self, target_kind_human: &str, warnings: &mut Vec<String>) {
- if self.crate_type.is_some() && self.crate_type2.is_some() {
- warn_on_deprecated(
- "crate-type",
- self.name().as_str(),
- format!("{target_kind_human} target").as_str(),
- warnings,
- );
+ if let Some(other_bo) = &profile.build_override {
+ match &mut self.build_override {
+ Some(self_bo) => self_bo.merge(other_bo),
+ None => self.build_override = Some(other_bo.clone()),
+ }
}
- }
- fn crate_types(&self) -> Option<&Vec<String>> {
- self.crate_type
- .as_ref()
- .or_else(|| self.crate_type2.as_ref())
- }
-}
+ if let Some(v) = &profile.inherits {
+ self.inherits = Some(v.clone());
+ }
-impl fmt::Debug for PathValue {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- self.0.fmt(f)
- }
-}
+ if let Some(v) = &profile.dir_name {
+ self.dir_name = Some(v.clone());
+ }
-#[derive(Deserialize, Serialize, Debug, Clone)]
-#[serde(expecting = "a lints table")]
-pub struct MaybeWorkspaceLints {
- #[serde(skip_serializing_if = "is_false")]
- #[serde(deserialize_with = "bool_no_false", default)]
- workspace: bool,
- #[serde(flatten)]
- lints: TomlLints,
-}
+ if let Some(v) = &profile.strip {
+ self.strip = Some(v.clone());
+ }
-fn is_false(b: &bool) -> bool {
- !b
+ if let Some(v) = &profile.trim_paths {
+ self.trim_paths = Some(v.clone())
+ }
+ }
}
-impl MaybeWorkspaceLints {
+impl schema::MaybeWorkspaceLints {
fn resolve<'a>(
self,
- get_ws_inheritable: impl FnOnce() -> CargoResult<TomlLints>,
- ) -> CargoResult<TomlLints> {
+ get_ws_inheritable: impl FnOnce() -> CargoResult<schema::TomlLints>,
+ ) -> CargoResult<schema::TomlLints> {
if self.workspace {
if !self.lints.is_empty() {
anyhow::bail!("cannot override `workspace.lints` in `lints`, either remove the overrides or `lints.workspace = true` and manually specify the lints");
@@ -3377,65 +2297,7 @@ impl MaybeWorkspaceLints {
}
}
-pub type TomlLints = BTreeMap<String, TomlToolLints>;
-
-pub type TomlToolLints = BTreeMap<String, TomlLint>;
-
-#[derive(Serialize, Debug, Clone)]
-#[serde(untagged)]
-pub enum TomlLint {
- Level(TomlLintLevel),
- Config(TomlLintConfig),
-}
-
-impl<'de> Deserialize<'de> for TomlLint {
- fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
- where
- D: de::Deserializer<'de>,
- {
- UntaggedEnumVisitor::new()
- .string(|string| {
- TomlLintLevel::deserialize(string.into_deserializer()).map(TomlLint::Level)
- })
- .map(|map| map.deserialize().map(TomlLint::Config))
- .deserialize(deserializer)
- }
-}
-
-impl TomlLint {
- fn level(&self) -> TomlLintLevel {
- match self {
- Self::Level(level) => *level,
- Self::Config(config) => config.level,
- }
- }
-
- fn priority(&self) -> i8 {
- match self {
- Self::Level(_) => 0,
- Self::Config(config) => config.priority,
- }
- }
-}
-
-#[derive(Serialize, Deserialize, Debug, Clone)]
-#[serde(rename_all = "kebab-case")]
-pub struct TomlLintConfig {
- level: TomlLintLevel,
- #[serde(default)]
- priority: i8,
-}
-
-#[derive(Serialize, Deserialize, Debug, Copy, Clone)]
-#[serde(rename_all = "kebab-case")]
-pub enum TomlLintLevel {
- Forbid,
- Deny,
- Warn,
- Allow,
-}
-
-impl TomlLintLevel {
+impl schema::TomlLintLevel {
fn flag(&self) -> &'static str {
match self {
Self::Forbid => "--forbid",
@@ -3446,19 +2308,18 @@ impl TomlLintLevel {
}
}
-#[derive(Copy, Clone, Debug)]
-#[non_exhaustive]
-struct InvalidCargoFeatures {}
+pub trait ResolveToPath {
+ fn resolve(&self, config: &Config) -> PathBuf;
+}
-impl<'de> de::Deserialize<'de> for InvalidCargoFeatures {
- fn deserialize<D>(_d: D) -> Result<Self, D::Error>
- where
- D: de::Deserializer<'de>,
- {
- use serde::de::Error as _;
+impl ResolveToPath for String {
+ fn resolve(&self, _: &Config) -> PathBuf {
+ self.into()
+ }
+}
- Err(D::Error::custom(
- "the field `cargo-features` should be set at the top of Cargo.toml before any tables",
- ))
+impl ResolveToPath for ConfigRelativePath {
+ fn resolve(&self, c: &Config) -> PathBuf {
+ self.resolve_path(c)
}
}
diff --git a/src/tools/cargo/src/cargo/util/toml/schema.rs b/src/tools/cargo/src/cargo/util/toml/schema.rs
new file mode 100644
index 000000000..6ea93e021
--- /dev/null
+++ b/src/tools/cargo/src/cargo/util/toml/schema.rs
@@ -0,0 +1,1189 @@
+use std::collections::BTreeMap;
+use std::fmt::{self, Display, Write};
+use std::path::PathBuf;
+use std::str;
+
+use serde::de::{self, IntoDeserializer as _, Unexpected};
+use serde::ser;
+use serde::{Deserialize, Serialize};
+use serde_untagged::UntaggedEnumVisitor;
+
+use crate::core::PackageIdSpec;
+use crate::util::RustVersion;
+
+/// This type is used to deserialize `Cargo.toml` files.
+#[derive(Debug, Deserialize, Serialize)]
+#[serde(rename_all = "kebab-case")]
+pub struct TomlManifest {
+ pub cargo_features: Option<Vec<String>>,
+ pub package: Option<Box<TomlPackage>>,
+ pub project: Option<Box<TomlPackage>>,
+ pub profile: Option<TomlProfiles>,
+ pub lib: Option<TomlLibTarget>,
+ pub bin: Option<Vec<TomlBinTarget>>,
+ pub example: Option<Vec<TomlExampleTarget>>,
+ pub test: Option<Vec<TomlTestTarget>>,
+ pub bench: Option<Vec<TomlTestTarget>>,
+ pub dependencies: Option<BTreeMap<String, MaybeWorkspaceDependency>>,
+ pub dev_dependencies: Option<BTreeMap<String, MaybeWorkspaceDependency>>,
+ #[serde(rename = "dev_dependencies")]
+ pub dev_dependencies2: Option<BTreeMap<String, MaybeWorkspaceDependency>>,
+ pub build_dependencies: Option<BTreeMap<String, MaybeWorkspaceDependency>>,
+ #[serde(rename = "build_dependencies")]
+ pub build_dependencies2: Option<BTreeMap<String, MaybeWorkspaceDependency>>,
+ pub features: Option<BTreeMap<String, Vec<String>>>,
+ pub target: Option<BTreeMap<String, TomlPlatform>>,
+ pub replace: Option<BTreeMap<String, TomlDependency>>,
+ pub patch: Option<BTreeMap<String, BTreeMap<String, TomlDependency>>>,
+ pub workspace: Option<TomlWorkspace>,
+ pub badges: Option<MaybeWorkspaceBtreeMap>,
+ pub lints: Option<MaybeWorkspaceLints>,
+}
+
+impl TomlManifest {
+ pub fn has_profiles(&self) -> bool {
+ self.profile.is_some()
+ }
+
+ pub fn dev_dependencies(&self) -> Option<&BTreeMap<String, MaybeWorkspaceDependency>> {
+ self.dev_dependencies
+ .as_ref()
+ .or(self.dev_dependencies2.as_ref())
+ }
+
+ pub fn build_dependencies(&self) -> Option<&BTreeMap<String, MaybeWorkspaceDependency>> {
+ self.build_dependencies
+ .as_ref()
+ .or(self.build_dependencies2.as_ref())
+ }
+
+ pub fn features(&self) -> Option<&BTreeMap<String, Vec<String>>> {
+ self.features.as_ref()
+ }
+}
+
+#[derive(Debug, Deserialize, Serialize, Clone)]
+#[serde(rename_all = "kebab-case")]
+pub struct TomlWorkspace {
+ pub members: Option<Vec<String>>,
+ pub exclude: Option<Vec<String>>,
+ pub default_members: Option<Vec<String>>,
+ pub resolver: Option<String>,
+ pub metadata: Option<toml::Value>,
+
+ // Properties that can be inherited by members.
+ pub package: Option<InheritableFields>,
+ pub dependencies: Option<BTreeMap<String, TomlDependency>>,
+ pub lints: Option<TomlLints>,
+}
+
+/// A group of fields that are inheritable by members of the workspace
+#[derive(Clone, Debug, Default, Deserialize, Serialize)]
+#[serde(rename_all = "kebab-case")]
+pub struct InheritableFields {
+ // We use skip here since it will never be present when deserializing
+ // and we don't want it present when serializing
+ #[serde(skip)]
+ pub dependencies: Option<BTreeMap<String, TomlDependency>>,
+ #[serde(skip)]
+ pub lints: Option<TomlLints>,
+
+ pub version: Option<semver::Version>,
+ pub authors: Option<Vec<String>>,
+ pub description: Option<String>,
+ pub homepage: Option<String>,
+ pub documentation: Option<String>,
+ pub readme: Option<StringOrBool>,
+ pub keywords: Option<Vec<String>>,
+ pub categories: Option<Vec<String>>,
+ pub license: Option<String>,
+ pub license_file: Option<String>,
+ pub repository: Option<String>,
+ pub publish: Option<VecStringOrBool>,
+ pub edition: Option<String>,
+ pub badges: Option<BTreeMap<String, BTreeMap<String, String>>>,
+ pub exclude: Option<Vec<String>>,
+ pub include: Option<Vec<String>>,
+ pub rust_version: Option<RustVersion>,
+ // We use skip here since it will never be present when deserializing
+ // and we don't want it present when serializing
+ #[serde(skip)]
+ pub ws_root: PathBuf,
+}
+
+/// Represents the `package`/`project` sections of a `Cargo.toml`.
+///
+/// Note that the order of the fields matters, since this is the order they
+/// are serialized to a TOML file. For example, you cannot have values after
+/// the field `metadata`, since it is a table and values cannot appear after
+/// tables.
+#[derive(Deserialize, Serialize, Clone, Debug)]
+#[serde(rename_all = "kebab-case")]
+pub struct TomlPackage {
+ pub edition: Option<MaybeWorkspaceString>,
+ pub rust_version: Option<MaybeWorkspaceRustVersion>,
+ pub name: String,
+ pub version: Option<MaybeWorkspaceSemverVersion>,
+ pub authors: Option<MaybeWorkspaceVecString>,
+ pub build: Option<StringOrBool>,
+ pub metabuild: Option<StringOrVec>,
+ pub default_target: Option<String>,
+ pub forced_target: Option<String>,
+ pub links: Option<String>,
+ pub exclude: Option<MaybeWorkspaceVecString>,
+ pub include: Option<MaybeWorkspaceVecString>,
+ pub publish: Option<MaybeWorkspaceVecStringOrBool>,
+ pub workspace: Option<String>,
+ pub im_a_teapot: Option<bool>,
+ pub autobins: Option<bool>,
+ pub autoexamples: Option<bool>,
+ pub autotests: Option<bool>,
+ pub autobenches: Option<bool>,
+ pub default_run: Option<String>,
+
+ // Package metadata.
+ pub description: Option<MaybeWorkspaceString>,
+ pub homepage: Option<MaybeWorkspaceString>,
+ pub documentation: Option<MaybeWorkspaceString>,
+ pub readme: Option<MaybeWorkspaceStringOrBool>,
+ pub keywords: Option<MaybeWorkspaceVecString>,
+ pub categories: Option<MaybeWorkspaceVecString>,
+ pub license: Option<MaybeWorkspaceString>,
+ pub license_file: Option<MaybeWorkspaceString>,
+ pub repository: Option<MaybeWorkspaceString>,
+ pub resolver: Option<String>,
+
+ pub metadata: Option<toml::Value>,
+
+ /// Provide a helpful error message for a common user error.
+ #[serde(rename = "cargo-features", skip_serializing)]
+ pub _invalid_cargo_features: Option<InvalidCargoFeatures>,
+}
+
+/// An enum that allows for inheriting keys from a workspace in a Cargo.toml.
+#[derive(Serialize, Copy, Clone, Debug)]
+#[serde(untagged)]
+pub enum MaybeWorkspace<T, W> {
+ /// The "defined" type, or the type that that is used when not inheriting from a workspace.
+ Defined(T),
+ /// The type when inheriting from a workspace.
+ Workspace(W),
+}
+
+//. This already has a `Deserialize` impl from version_trim_whitespace
+pub type MaybeWorkspaceSemverVersion = MaybeWorkspace<semver::Version, TomlWorkspaceField>;
+impl<'de> de::Deserialize<'de> for MaybeWorkspaceSemverVersion {
+ fn deserialize<D>(d: D) -> Result<Self, D::Error>
+ where
+ D: de::Deserializer<'de>,
+ {
+ UntaggedEnumVisitor::new()
+ .expecting("SemVer version")
+ .string(
+ |value| match value.trim().parse().map_err(de::Error::custom) {
+ Ok(parsed) => Ok(MaybeWorkspace::Defined(parsed)),
+ Err(e) => Err(e),
+ },
+ )
+ .map(|value| value.deserialize().map(MaybeWorkspace::Workspace))
+ .deserialize(d)
+ }
+}
+
+pub type MaybeWorkspaceString = MaybeWorkspace<String, TomlWorkspaceField>;
+impl<'de> de::Deserialize<'de> for MaybeWorkspaceString {
+ fn deserialize<D>(d: D) -> Result<Self, D::Error>
+ where
+ D: de::Deserializer<'de>,
+ {
+ struct Visitor;
+
+ impl<'de> de::Visitor<'de> for Visitor {
+ type Value = MaybeWorkspaceString;
+
+ fn expecting(&self, f: &mut fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
+ f.write_str("a string or workspace")
+ }
+
+ fn visit_string<E>(self, value: String) -> Result<Self::Value, E>
+ where
+ E: de::Error,
+ {
+ Ok(MaybeWorkspaceString::Defined(value))
+ }
+
+ fn visit_map<V>(self, map: V) -> Result<Self::Value, V::Error>
+ where
+ V: de::MapAccess<'de>,
+ {
+ let mvd = de::value::MapAccessDeserializer::new(map);
+ TomlWorkspaceField::deserialize(mvd).map(MaybeWorkspace::Workspace)
+ }
+ }
+
+ d.deserialize_any(Visitor)
+ }
+}
+
+pub type MaybeWorkspaceRustVersion = MaybeWorkspace<RustVersion, TomlWorkspaceField>;
+impl<'de> de::Deserialize<'de> for MaybeWorkspaceRustVersion {
+ fn deserialize<D>(d: D) -> Result<Self, D::Error>
+ where
+ D: de::Deserializer<'de>,
+ {
+ struct Visitor;
+
+ impl<'de> de::Visitor<'de> for Visitor {
+ type Value = MaybeWorkspaceRustVersion;
+
+ fn expecting(&self, f: &mut fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
+ f.write_str("a semver or workspace")
+ }
+
+ fn visit_string<E>(self, value: String) -> Result<Self::Value, E>
+ where
+ E: de::Error,
+ {
+ let value = value.parse::<RustVersion>().map_err(|e| E::custom(e))?;
+ Ok(MaybeWorkspaceRustVersion::Defined(value))
+ }
+
+ fn visit_map<V>(self, map: V) -> Result<Self::Value, V::Error>
+ where
+ V: de::MapAccess<'de>,
+ {
+ let mvd = de::value::MapAccessDeserializer::new(map);
+ TomlWorkspaceField::deserialize(mvd).map(MaybeWorkspace::Workspace)
+ }
+ }
+
+ d.deserialize_any(Visitor)
+ }
+}
+
+pub type MaybeWorkspaceVecString = MaybeWorkspace<Vec<String>, TomlWorkspaceField>;
+impl<'de> de::Deserialize<'de> for MaybeWorkspaceVecString {
+ fn deserialize<D>(d: D) -> Result<Self, D::Error>
+ where
+ D: de::Deserializer<'de>,
+ {
+ struct Visitor;
+
+ impl<'de> de::Visitor<'de> for Visitor {
+ type Value = MaybeWorkspaceVecString;
+
+ fn expecting(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
+ f.write_str("a vector of strings or workspace")
+ }
+ fn visit_seq<A>(self, v: A) -> Result<Self::Value, A::Error>
+ where
+ A: de::SeqAccess<'de>,
+ {
+ let seq = de::value::SeqAccessDeserializer::new(v);
+ Vec::deserialize(seq).map(MaybeWorkspace::Defined)
+ }
+
+ fn visit_map<V>(self, map: V) -> Result<Self::Value, V::Error>
+ where
+ V: de::MapAccess<'de>,
+ {
+ let mvd = de::value::MapAccessDeserializer::new(map);
+ TomlWorkspaceField::deserialize(mvd).map(MaybeWorkspace::Workspace)
+ }
+ }
+
+ d.deserialize_any(Visitor)
+ }
+}
+
+pub type MaybeWorkspaceStringOrBool = MaybeWorkspace<StringOrBool, TomlWorkspaceField>;
+impl<'de> de::Deserialize<'de> for MaybeWorkspaceStringOrBool {
+ fn deserialize<D>(d: D) -> Result<Self, D::Error>
+ where
+ D: de::Deserializer<'de>,
+ {
+ struct Visitor;
+
+ impl<'de> de::Visitor<'de> for Visitor {
+ type Value = MaybeWorkspaceStringOrBool;
+
+ fn expecting(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
+ f.write_str("a string, a bool, or workspace")
+ }
+
+ fn visit_bool<E>(self, v: bool) -> Result<Self::Value, E>
+ where
+ E: de::Error,
+ {
+ let b = de::value::BoolDeserializer::new(v);
+ StringOrBool::deserialize(b).map(MaybeWorkspace::Defined)
+ }
+
+ fn visit_string<E>(self, v: String) -> Result<Self::Value, E>
+ where
+ E: de::Error,
+ {
+ let string = de::value::StringDeserializer::new(v);
+ StringOrBool::deserialize(string).map(MaybeWorkspace::Defined)
+ }
+
+ fn visit_map<V>(self, map: V) -> Result<Self::Value, V::Error>
+ where
+ V: de::MapAccess<'de>,
+ {
+ let mvd = de::value::MapAccessDeserializer::new(map);
+ TomlWorkspaceField::deserialize(mvd).map(MaybeWorkspace::Workspace)
+ }
+ }
+
+ d.deserialize_any(Visitor)
+ }
+}
+
+pub type MaybeWorkspaceVecStringOrBool = MaybeWorkspace<VecStringOrBool, TomlWorkspaceField>;
+impl<'de> de::Deserialize<'de> for MaybeWorkspaceVecStringOrBool {
+ fn deserialize<D>(d: D) -> Result<Self, D::Error>
+ where
+ D: de::Deserializer<'de>,
+ {
+ struct Visitor;
+
+ impl<'de> de::Visitor<'de> for Visitor {
+ type Value = MaybeWorkspaceVecStringOrBool;
+
+ fn expecting(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
+ f.write_str("a boolean, a vector of strings, or workspace")
+ }
+
+ fn visit_bool<E>(self, v: bool) -> Result<Self::Value, E>
+ where
+ E: de::Error,
+ {
+ let b = de::value::BoolDeserializer::new(v);
+ VecStringOrBool::deserialize(b).map(MaybeWorkspace::Defined)
+ }
+
+ fn visit_seq<A>(self, v: A) -> Result<Self::Value, A::Error>
+ where
+ A: de::SeqAccess<'de>,
+ {
+ let seq = de::value::SeqAccessDeserializer::new(v);
+ VecStringOrBool::deserialize(seq).map(MaybeWorkspace::Defined)
+ }
+
+ fn visit_map<V>(self, map: V) -> Result<Self::Value, V::Error>
+ where
+ V: de::MapAccess<'de>,
+ {
+ let mvd = de::value::MapAccessDeserializer::new(map);
+ TomlWorkspaceField::deserialize(mvd).map(MaybeWorkspace::Workspace)
+ }
+ }
+
+ d.deserialize_any(Visitor)
+ }
+}
+
+pub type MaybeWorkspaceBtreeMap =
+ MaybeWorkspace<BTreeMap<String, BTreeMap<String, String>>, TomlWorkspaceField>;
+
+impl<'de> de::Deserialize<'de> for MaybeWorkspaceBtreeMap {
+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+ where
+ D: de::Deserializer<'de>,
+ {
+ let value = serde_value::Value::deserialize(deserializer)?;
+
+ if let Ok(w) = TomlWorkspaceField::deserialize(
+ serde_value::ValueDeserializer::<D::Error>::new(value.clone()),
+ ) {
+ return if w.workspace {
+ Ok(MaybeWorkspace::Workspace(w))
+ } else {
+ Err(de::Error::custom("`workspace` cannot be false"))
+ };
+ }
+ BTreeMap::deserialize(serde_value::ValueDeserializer::<D::Error>::new(value))
+ .map(MaybeWorkspace::Defined)
+ }
+}
+
+#[derive(Deserialize, Serialize, Copy, Clone, Debug)]
+#[serde(rename_all = "kebab-case")]
+pub struct TomlWorkspaceField {
+ #[serde(deserialize_with = "bool_no_false")]
+ pub workspace: bool,
+}
+
+fn bool_no_false<'de, D: de::Deserializer<'de>>(deserializer: D) -> Result<bool, D::Error> {
+ let b: bool = Deserialize::deserialize(deserializer)?;
+ if b {
+ Ok(b)
+ } else {
+ Err(de::Error::custom("`workspace` cannot be false"))
+ }
+}
+
+pub type MaybeWorkspaceDependency = MaybeWorkspace<TomlDependency, TomlWorkspaceDependency>;
+
+impl MaybeWorkspaceDependency {
+ pub fn unused_keys(&self) -> Vec<String> {
+ match self {
+ MaybeWorkspaceDependency::Defined(d) => d.unused_keys(),
+ MaybeWorkspaceDependency::Workspace(w) => w.unused_keys.keys().cloned().collect(),
+ }
+ }
+}
+
+impl<'de> de::Deserialize<'de> for MaybeWorkspaceDependency {
+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+ where
+ D: de::Deserializer<'de>,
+ {
+ let value = serde_value::Value::deserialize(deserializer)?;
+
+ if let Ok(w) = TomlWorkspaceDependency::deserialize(serde_value::ValueDeserializer::<
+ D::Error,
+ >::new(value.clone()))
+ {
+ return if w.workspace {
+ Ok(MaybeWorkspace::Workspace(w))
+ } else {
+ Err(de::Error::custom("`workspace` cannot be false"))
+ };
+ }
+ TomlDependency::deserialize(serde_value::ValueDeserializer::<D::Error>::new(value))
+ .map(MaybeWorkspace::Defined)
+ }
+}
+
+#[derive(Deserialize, Serialize, Clone, Debug)]
+#[serde(rename_all = "kebab-case")]
+pub struct TomlWorkspaceDependency {
+ pub workspace: bool,
+ pub features: Option<Vec<String>>,
+ pub default_features: Option<bool>,
+ #[serde(rename = "default_features")]
+ pub default_features2: Option<bool>,
+ pub optional: Option<bool>,
+ pub public: Option<bool>,
+
+ /// This is here to provide a way to see the "unused manifest keys" when deserializing
+ #[serde(skip_serializing)]
+ #[serde(flatten)]
+ pub unused_keys: BTreeMap<String, toml::Value>,
+}
+
+impl TomlWorkspaceDependency {
+ pub fn default_features(&self) -> Option<bool> {
+ self.default_features.or(self.default_features2)
+ }
+}
+
+#[derive(Clone, Debug, Serialize)]
+#[serde(untagged)]
+pub enum TomlDependency<P: Clone = String> {
+ /// In the simple format, only a version is specified, eg.
+ /// `package = "<version>"`
+ Simple(String),
+ /// The simple format is equivalent to a detailed dependency
+ /// specifying only a version, eg.
+ /// `package = { version = "<version>" }`
+ Detailed(DetailedTomlDependency<P>),
+}
+
+impl TomlDependency {
+ pub fn is_version_specified(&self) -> bool {
+ match self {
+ TomlDependency::Detailed(d) => d.version.is_some(),
+ TomlDependency::Simple(..) => true,
+ }
+ }
+
+ pub fn is_optional(&self) -> bool {
+ match self {
+ TomlDependency::Detailed(d) => d.optional.unwrap_or(false),
+ TomlDependency::Simple(..) => false,
+ }
+ }
+
+ pub fn unused_keys(&self) -> Vec<String> {
+ match self {
+ TomlDependency::Simple(_) => vec![],
+ TomlDependency::Detailed(detailed) => detailed.unused_keys.keys().cloned().collect(),
+ }
+ }
+}
+
+impl<'de, P: Deserialize<'de> + Clone> de::Deserialize<'de> for TomlDependency<P> {
+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+ where
+ D: de::Deserializer<'de>,
+ {
+ UntaggedEnumVisitor::new()
+ .expecting(
+ "a version string like \"0.9.8\" or a \
+ detailed dependency like { version = \"0.9.8\" }",
+ )
+ .string(|value| Ok(TomlDependency::Simple(value.to_owned())))
+ .map(|value| value.deserialize().map(TomlDependency::Detailed))
+ .deserialize(deserializer)
+ }
+}
+
+#[derive(Deserialize, Serialize, Clone, Debug)]
+#[serde(rename_all = "kebab-case")]
+pub struct DetailedTomlDependency<P: Clone = String> {
+ pub version: Option<String>,
+ pub registry: Option<String>,
+ /// The URL of the `registry` field.
+ /// This is an internal implementation detail. When Cargo creates a
+ /// package, it replaces `registry` with `registry-index` so that the
+ /// manifest contains the correct URL. All users won't have the same
+ /// registry names configured, so Cargo can't rely on just the name for
+ /// crates published by other users.
+ pub registry_index: Option<String>,
+ // `path` is relative to the file it appears in. If that's a `Cargo.toml`, it'll be relative to
+ // that TOML file, and if it's a `.cargo/config` file, it'll be relative to that file.
+ pub path: Option<P>,
+ pub git: Option<String>,
+ pub branch: Option<String>,
+ pub tag: Option<String>,
+ pub rev: Option<String>,
+ pub features: Option<Vec<String>>,
+ pub optional: Option<bool>,
+ pub default_features: Option<bool>,
+ #[serde(rename = "default_features")]
+ pub default_features2: Option<bool>,
+ pub package: Option<String>,
+ pub public: Option<bool>,
+
+ /// One or more of `bin`, `cdylib`, `staticlib`, `bin:<name>`.
+ pub artifact: Option<StringOrVec>,
+ /// If set, the artifact should also be a dependency
+ pub lib: Option<bool>,
+ /// A platform name, like `x86_64-apple-darwin`
+ pub target: Option<String>,
+
+ /// This is here to provide a way to see the "unused manifest keys" when deserializing
+ #[serde(skip_serializing)]
+ #[serde(flatten)]
+ pub unused_keys: BTreeMap<String, toml::Value>,
+}
+
+impl<P: Clone> DetailedTomlDependency<P> {
+ pub fn default_features(&self) -> Option<bool> {
+ self.default_features.or(self.default_features2)
+ }
+}
+
+// Explicit implementation so we avoid pulling in P: Default
+impl<P: Clone> Default for DetailedTomlDependency<P> {
+ fn default() -> Self {
+ Self {
+ version: Default::default(),
+ registry: Default::default(),
+ registry_index: Default::default(),
+ path: Default::default(),
+ git: Default::default(),
+ branch: Default::default(),
+ tag: Default::default(),
+ rev: Default::default(),
+ features: Default::default(),
+ optional: Default::default(),
+ default_features: Default::default(),
+ default_features2: Default::default(),
+ package: Default::default(),
+ public: Default::default(),
+ artifact: Default::default(),
+ lib: Default::default(),
+ target: Default::default(),
+ unused_keys: Default::default(),
+ }
+ }
+}
+
+#[derive(Deserialize, Serialize, Clone, Debug, Default)]
+pub struct TomlProfiles(pub BTreeMap<String, TomlProfile>);
+
+impl TomlProfiles {
+ pub fn get_all(&self) -> &BTreeMap<String, TomlProfile> {
+ &self.0
+ }
+
+ pub fn get(&self, name: &str) -> Option<&TomlProfile> {
+ self.0.get(name)
+ }
+}
+
+#[derive(Deserialize, Serialize, Clone, Debug, Default, Eq, PartialEq)]
+#[serde(default, rename_all = "kebab-case")]
+pub struct TomlProfile {
+ pub opt_level: Option<TomlOptLevel>,
+ pub lto: Option<StringOrBool>,
+ pub codegen_backend: Option<String>,
+ pub codegen_units: Option<u32>,
+ pub debug: Option<TomlDebugInfo>,
+ pub split_debuginfo: Option<String>,
+ pub debug_assertions: Option<bool>,
+ pub rpath: Option<bool>,
+ pub panic: Option<String>,
+ pub overflow_checks: Option<bool>,
+ pub incremental: Option<bool>,
+ pub dir_name: Option<String>,
+ pub inherits: Option<String>,
+ pub strip: Option<StringOrBool>,
+ // Note that `rustflags` is used for the cargo-feature `profile_rustflags`
+ pub rustflags: Option<Vec<String>>,
+ // These two fields must be last because they are sub-tables, and TOML
+ // requires all non-tables to be listed first.
+ pub package: Option<BTreeMap<ProfilePackageSpec, TomlProfile>>,
+ pub build_override: Option<Box<TomlProfile>>,
+ /// Unstable feature `-Ztrim-paths`.
+ pub trim_paths: Option<TomlTrimPaths>,
+}
+
+#[derive(Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]
+pub enum ProfilePackageSpec {
+ Spec(PackageIdSpec),
+ All,
+}
+
+impl fmt::Display for ProfilePackageSpec {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match self {
+ ProfilePackageSpec::Spec(spec) => spec.fmt(f),
+ ProfilePackageSpec::All => f.write_str("*"),
+ }
+ }
+}
+
+impl ser::Serialize for ProfilePackageSpec {
+ fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
+ where
+ S: ser::Serializer,
+ {
+ self.to_string().serialize(s)
+ }
+}
+
+impl<'de> de::Deserialize<'de> for ProfilePackageSpec {
+ fn deserialize<D>(d: D) -> Result<ProfilePackageSpec, D::Error>
+ where
+ D: de::Deserializer<'de>,
+ {
+ let string = String::deserialize(d)?;
+ if string == "*" {
+ Ok(ProfilePackageSpec::All)
+ } else {
+ PackageIdSpec::parse(&string)
+ .map_err(de::Error::custom)
+ .map(ProfilePackageSpec::Spec)
+ }
+ }
+}
+
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct TomlOptLevel(pub String);
+
+impl ser::Serialize for TomlOptLevel {
+ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+ where
+ S: ser::Serializer,
+ {
+ match self.0.parse::<u32>() {
+ Ok(n) => n.serialize(serializer),
+ Err(_) => self.0.serialize(serializer),
+ }
+ }
+}
+
+impl<'de> de::Deserialize<'de> for TomlOptLevel {
+ fn deserialize<D>(d: D) -> Result<TomlOptLevel, D::Error>
+ where
+ D: de::Deserializer<'de>,
+ {
+ use serde::de::Error as _;
+ UntaggedEnumVisitor::new()
+ .expecting("an optimization level")
+ .i64(|value| Ok(TomlOptLevel(value.to_string())))
+ .string(|value| {
+ if value == "s" || value == "z" {
+ Ok(TomlOptLevel(value.to_string()))
+ } else {
+ Err(serde_untagged::de::Error::custom(format!(
+ "must be `0`, `1`, `2`, `3`, `s` or `z`, \
+ but found the string: \"{}\"",
+ value
+ )))
+ }
+ })
+ .deserialize(d)
+ }
+}
+
+#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, PartialOrd, Ord)]
+pub enum TomlDebugInfo {
+ None,
+ LineDirectivesOnly,
+ LineTablesOnly,
+ Limited,
+ Full,
+}
+
+impl Display for TomlDebugInfo {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match self {
+ TomlDebugInfo::None => f.write_char('0'),
+ TomlDebugInfo::Limited => f.write_char('1'),
+ TomlDebugInfo::Full => f.write_char('2'),
+ TomlDebugInfo::LineDirectivesOnly => f.write_str("line-directives-only"),
+ TomlDebugInfo::LineTablesOnly => f.write_str("line-tables-only"),
+ }
+ }
+}
+
+impl ser::Serialize for TomlDebugInfo {
+ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+ where
+ S: ser::Serializer,
+ {
+ match self {
+ Self::None => 0.serialize(serializer),
+ Self::LineDirectivesOnly => "line-directives-only".serialize(serializer),
+ Self::LineTablesOnly => "line-tables-only".serialize(serializer),
+ Self::Limited => 1.serialize(serializer),
+ Self::Full => 2.serialize(serializer),
+ }
+ }
+}
+
+impl<'de> de::Deserialize<'de> for TomlDebugInfo {
+ fn deserialize<D>(d: D) -> Result<TomlDebugInfo, D::Error>
+ where
+ D: de::Deserializer<'de>,
+ {
+ use serde::de::Error as _;
+ let expecting = "a boolean, 0, 1, 2, \"line-tables-only\", or \"line-directives-only\"";
+ UntaggedEnumVisitor::new()
+ .expecting(expecting)
+ .bool(|value| {
+ Ok(if value {
+ TomlDebugInfo::Full
+ } else {
+ TomlDebugInfo::None
+ })
+ })
+ .i64(|value| {
+ let debuginfo = match value {
+ 0 => TomlDebugInfo::None,
+ 1 => TomlDebugInfo::Limited,
+ 2 => TomlDebugInfo::Full,
+ _ => {
+ return Err(serde_untagged::de::Error::invalid_value(
+ Unexpected::Signed(value),
+ &expecting,
+ ))
+ }
+ };
+ Ok(debuginfo)
+ })
+ .string(|value| {
+ let debuginfo = match value {
+ "none" => TomlDebugInfo::None,
+ "limited" => TomlDebugInfo::Limited,
+ "full" => TomlDebugInfo::Full,
+ "line-directives-only" => TomlDebugInfo::LineDirectivesOnly,
+ "line-tables-only" => TomlDebugInfo::LineTablesOnly,
+ _ => {
+ return Err(serde_untagged::de::Error::invalid_value(
+ Unexpected::Str(value),
+ &expecting,
+ ))
+ }
+ };
+ Ok(debuginfo)
+ })
+ .deserialize(d)
+ }
+}
+
+#[derive(Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash, Serialize)]
+#[serde(untagged, rename_all = "kebab-case")]
+pub enum TomlTrimPaths {
+ Values(Vec<TomlTrimPathsValue>),
+ All,
+}
+
+impl TomlTrimPaths {
+ pub fn none() -> Self {
+ TomlTrimPaths::Values(Vec::new())
+ }
+
+ pub fn is_none(&self) -> bool {
+ match self {
+ TomlTrimPaths::Values(v) => v.is_empty(),
+ TomlTrimPaths::All => false,
+ }
+ }
+}
+
+impl<'de> de::Deserialize<'de> for TomlTrimPaths {
+ fn deserialize<D>(d: D) -> Result<TomlTrimPaths, D::Error>
+ where
+ D: de::Deserializer<'de>,
+ {
+ use serde::de::Error as _;
+ let expecting = r#"a boolean, "none", "diagnostics", "macro", "object", "all", or an array with these options"#;
+ UntaggedEnumVisitor::new()
+ .expecting(expecting)
+ .bool(|value| {
+ Ok(if value {
+ TomlTrimPaths::All
+ } else {
+ TomlTrimPaths::none()
+ })
+ })
+ .string(|v| match v {
+ "none" => Ok(TomlTrimPaths::none()),
+ "all" => Ok(TomlTrimPaths::All),
+ v => {
+ let d = v.into_deserializer();
+ let err = |_: D::Error| {
+ serde_untagged::de::Error::custom(format!("expected {expecting}"))
+ };
+ TomlTrimPathsValue::deserialize(d)
+ .map_err(err)
+ .map(|v| v.into())
+ }
+ })
+ .seq(|seq| {
+ let seq: Vec<String> = seq.deserialize()?;
+ let seq: Vec<_> = seq
+ .into_iter()
+ .map(|s| TomlTrimPathsValue::deserialize(s.into_deserializer()))
+ .collect::<Result<_, _>>()?;
+ Ok(seq.into())
+ })
+ .deserialize(d)
+ }
+}
+
+impl fmt::Display for TomlTrimPaths {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match self {
+ TomlTrimPaths::All => write!(f, "all"),
+ TomlTrimPaths::Values(v) if v.is_empty() => write!(f, "none"),
+ TomlTrimPaths::Values(v) => {
+ let mut iter = v.iter();
+ if let Some(value) = iter.next() {
+ write!(f, "{value}")?;
+ }
+ for value in iter {
+ write!(f, ",{value}")?;
+ }
+ Ok(())
+ }
+ }
+ }
+}
+
+impl From<TomlTrimPathsValue> for TomlTrimPaths {
+ fn from(value: TomlTrimPathsValue) -> Self {
+ TomlTrimPaths::Values(vec![value])
+ }
+}
+
+impl From<Vec<TomlTrimPathsValue>> for TomlTrimPaths {
+ fn from(value: Vec<TomlTrimPathsValue>) -> Self {
+ TomlTrimPaths::Values(value)
+ }
+}
+
+#[derive(Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash, Serialize, Deserialize)]
+#[serde(rename_all = "kebab-case")]
+pub enum TomlTrimPathsValue {
+ Diagnostics,
+ Macro,
+ Object,
+}
+
+impl TomlTrimPathsValue {
+ pub fn as_str(&self) -> &'static str {
+ match self {
+ TomlTrimPathsValue::Diagnostics => "diagnostics",
+ TomlTrimPathsValue::Macro => "macro",
+ TomlTrimPathsValue::Object => "object",
+ }
+ }
+}
+
+impl fmt::Display for TomlTrimPathsValue {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "{}", self.as_str())
+ }
+}
+
+pub type TomlLibTarget = TomlTarget;
+pub type TomlBinTarget = TomlTarget;
+pub type TomlExampleTarget = TomlTarget;
+pub type TomlTestTarget = TomlTarget;
+pub type TomlBenchTarget = TomlTarget;
+
+#[derive(Default, Serialize, Deserialize, Debug, Clone)]
+#[serde(rename_all = "kebab-case")]
+pub struct TomlTarget {
+ pub name: Option<String>,
+
+ // The intention was to only accept `crate-type` here but historical
+ // versions of Cargo also accepted `crate_type`, so look for both.
+ pub crate_type: Option<Vec<String>>,
+ #[serde(rename = "crate_type")]
+ pub crate_type2: Option<Vec<String>>,
+
+ pub path: Option<PathValue>,
+ // Note that `filename` is used for the cargo-feature `different_binary_name`
+ pub filename: Option<String>,
+ pub test: Option<bool>,
+ pub doctest: Option<bool>,
+ pub bench: Option<bool>,
+ pub doc: Option<bool>,
+ pub plugin: Option<bool>,
+ pub doc_scrape_examples: Option<bool>,
+ #[serde(rename = "proc-macro")]
+ pub proc_macro_raw: Option<bool>,
+ #[serde(rename = "proc_macro")]
+ pub proc_macro_raw2: Option<bool>,
+ pub harness: Option<bool>,
+ pub required_features: Option<Vec<String>>,
+ pub edition: Option<String>,
+}
+
+impl TomlTarget {
+ pub fn new() -> TomlTarget {
+ TomlTarget::default()
+ }
+
+ pub fn proc_macro(&self) -> Option<bool> {
+ self.proc_macro_raw.or(self.proc_macro_raw2).or_else(|| {
+ if let Some(types) = self.crate_types() {
+ if types.contains(&"proc-macro".to_string()) {
+ return Some(true);
+ }
+ }
+ None
+ })
+ }
+
+ pub fn crate_types(&self) -> Option<&Vec<String>> {
+ self.crate_type
+ .as_ref()
+ .or_else(|| self.crate_type2.as_ref())
+ }
+}
+
+/// Corresponds to a `target` entry, but `TomlTarget` is already used.
+#[derive(Serialize, Deserialize, Debug, Clone)]
+#[serde(rename_all = "kebab-case")]
+pub struct TomlPlatform {
+ pub dependencies: Option<BTreeMap<String, MaybeWorkspaceDependency>>,
+ pub build_dependencies: Option<BTreeMap<String, MaybeWorkspaceDependency>>,
+ #[serde(rename = "build_dependencies")]
+ pub build_dependencies2: Option<BTreeMap<String, MaybeWorkspaceDependency>>,
+ pub dev_dependencies: Option<BTreeMap<String, MaybeWorkspaceDependency>>,
+ #[serde(rename = "dev_dependencies")]
+ pub dev_dependencies2: Option<BTreeMap<String, MaybeWorkspaceDependency>>,
+}
+
+impl TomlPlatform {
+ pub fn dev_dependencies(&self) -> Option<&BTreeMap<String, MaybeWorkspaceDependency>> {
+ self.dev_dependencies
+ .as_ref()
+ .or(self.dev_dependencies2.as_ref())
+ }
+
+ pub fn build_dependencies(&self) -> Option<&BTreeMap<String, MaybeWorkspaceDependency>> {
+ self.build_dependencies
+ .as_ref()
+ .or(self.build_dependencies2.as_ref())
+ }
+}
+
+#[derive(Deserialize, Serialize, Debug, Clone)]
+#[serde(expecting = "a lints table")]
+#[serde(rename_all = "kebab-case")]
+pub struct MaybeWorkspaceLints {
+ #[serde(skip_serializing_if = "is_false")]
+ #[serde(deserialize_with = "bool_no_false", default)]
+ pub workspace: bool,
+ #[serde(flatten)]
+ pub lints: TomlLints,
+}
+
+fn is_false(b: &bool) -> bool {
+ !b
+}
+
+pub type TomlLints = BTreeMap<String, TomlToolLints>;
+
+pub type TomlToolLints = BTreeMap<String, TomlLint>;
+
+#[derive(Serialize, Debug, Clone)]
+#[serde(untagged)]
+pub enum TomlLint {
+ Level(TomlLintLevel),
+ Config(TomlLintConfig),
+}
+
+impl<'de> Deserialize<'de> for TomlLint {
+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+ where
+ D: de::Deserializer<'de>,
+ {
+ UntaggedEnumVisitor::new()
+ .string(|string| {
+ TomlLintLevel::deserialize(string.into_deserializer()).map(TomlLint::Level)
+ })
+ .map(|map| map.deserialize().map(TomlLint::Config))
+ .deserialize(deserializer)
+ }
+}
+
+impl TomlLint {
+ pub fn level(&self) -> TomlLintLevel {
+ match self {
+ Self::Level(level) => *level,
+ Self::Config(config) => config.level,
+ }
+ }
+
+ pub fn priority(&self) -> i8 {
+ match self {
+ Self::Level(_) => 0,
+ Self::Config(config) => config.priority,
+ }
+ }
+}
+
+#[derive(Serialize, Deserialize, Debug, Clone)]
+#[serde(rename_all = "kebab-case")]
+pub struct TomlLintConfig {
+ pub level: TomlLintLevel,
+ #[serde(default)]
+ pub priority: i8,
+}
+
+#[derive(Serialize, Deserialize, Debug, Copy, Clone)]
+#[serde(rename_all = "kebab-case")]
+pub enum TomlLintLevel {
+ Forbid,
+ Deny,
+ Warn,
+ Allow,
+}
+
+#[derive(Copy, Clone, Debug)]
+pub struct InvalidCargoFeatures {}
+
+impl<'de> de::Deserialize<'de> for InvalidCargoFeatures {
+ fn deserialize<D>(_d: D) -> Result<Self, D::Error>
+ where
+ D: de::Deserializer<'de>,
+ {
+ use serde::de::Error as _;
+
+ Err(D::Error::custom(
+ "the field `cargo-features` should be set at the top of Cargo.toml before any tables",
+ ))
+ }
+}
+
+/// A StringOrVec can be parsed from either a TOML string or array,
+/// but is always stored as a vector.
+#[derive(Clone, Debug, Serialize, Eq, PartialEq, PartialOrd, Ord)]
+pub struct StringOrVec(pub Vec<String>);
+
+impl StringOrVec {
+ pub fn iter<'a>(&'a self) -> std::slice::Iter<'a, String> {
+ self.0.iter()
+ }
+}
+
+impl<'de> de::Deserialize<'de> for StringOrVec {
+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+ where
+ D: de::Deserializer<'de>,
+ {
+ UntaggedEnumVisitor::new()
+ .expecting("string or list of strings")
+ .string(|value| Ok(StringOrVec(vec![value.to_owned()])))
+ .seq(|value| value.deserialize().map(StringOrVec))
+ .deserialize(deserializer)
+ }
+}
+
+#[derive(Clone, Debug, Serialize, Eq, PartialEq)]
+#[serde(untagged)]
+pub enum StringOrBool {
+ String(String),
+ Bool(bool),
+}
+
+impl<'de> Deserialize<'de> for StringOrBool {
+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+ where
+ D: de::Deserializer<'de>,
+ {
+ UntaggedEnumVisitor::new()
+ .bool(|b| Ok(StringOrBool::Bool(b)))
+ .string(|s| Ok(StringOrBool::String(s.to_owned())))
+ .deserialize(deserializer)
+ }
+}
+
+#[derive(PartialEq, Clone, Debug, Serialize)]
+#[serde(untagged)]
+pub enum VecStringOrBool {
+ VecString(Vec<String>),
+ Bool(bool),
+}
+
+impl<'de> de::Deserialize<'de> for VecStringOrBool {
+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+ where
+ D: de::Deserializer<'de>,
+ {
+ UntaggedEnumVisitor::new()
+ .expecting("a boolean or vector of strings")
+ .bool(|value| Ok(VecStringOrBool::Bool(value)))
+ .seq(|value| value.deserialize().map(VecStringOrBool::VecString))
+ .deserialize(deserializer)
+ }
+}
+
+#[derive(Clone)]
+pub struct PathValue(pub PathBuf);
+
+impl fmt::Debug for PathValue {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ self.0.fmt(f)
+ }
+}
+
+impl ser::Serialize for PathValue {
+ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+ where
+ S: ser::Serializer,
+ {
+ self.0.serialize(serializer)
+ }
+}
+
+impl<'de> de::Deserialize<'de> for PathValue {
+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+ where
+ D: de::Deserializer<'de>,
+ {
+ Ok(PathValue(String::deserialize(deserializer)?.into()))
+ }
+}
diff --git a/src/tools/cargo/src/cargo/util/toml/targets.rs b/src/tools/cargo/src/cargo/util/toml/targets.rs
index afc4f258f..9d456ffd7 100644
--- a/src/tools/cargo/src/cargo/util/toml/targets.rs
+++ b/src/tools/cargo/src/cargo/util/toml/targets.rs
@@ -14,7 +14,7 @@ use std::collections::HashSet;
use std::fs::{self, DirEntry};
use std::path::{Path, PathBuf};
-use super::{
+use super::schema::{
PathValue, StringOrBool, StringOrVec, TomlBenchTarget, TomlBinTarget, TomlExampleTarget,
TomlLibTarget, TomlManifest, TomlTarget, TomlTestTarget,
};
@@ -23,6 +23,7 @@ use crate::core::compiler::CrateType;
use crate::core::{Edition, Feature, Features, Target};
use crate::util::errors::CargoResult;
use crate::util::restricted_names;
+use crate::util::toml::warn_on_deprecated;
use anyhow::Context as _;
@@ -31,7 +32,7 @@ const DEFAULT_BENCH_DIR_NAME: &'static str = "benches";
const DEFAULT_EXAMPLE_DIR_NAME: &'static str = "examples";
const DEFAULT_BIN_DIR_NAME: &'static str = "bin";
-pub fn targets(
+pub(super) fn targets(
features: &Features,
manifest: &TomlManifest,
package_name: &str,
@@ -105,7 +106,7 @@ pub fn targets(
)?);
// processing the custom build script
- if let Some(custom_build) = manifest.maybe_custom_build(custom_build, package_root) {
+ if let Some(custom_build) = maybe_custom_build(custom_build, package_root) {
if metabuild.is_some() {
anyhow::bail!("cannot specify both `metabuild` and `build`");
}
@@ -172,8 +173,8 @@ fn clean_lib(
};
let Some(ref lib) = lib else { return Ok(None) };
- lib.validate_proc_macro(warnings);
- lib.validate_crate_types("library", warnings);
+ validate_proc_macro(lib, "library", warnings);
+ validate_crate_types(lib, "library", warnings);
validate_target_name(lib, "library", "lib", warnings)?;
@@ -181,20 +182,22 @@ fn clean_lib(
(Some(path), _) => package_root.join(&path.0),
(None, Some(path)) => path,
(None, None) => {
- let legacy_path = package_root.join("src").join(format!("{}.rs", lib.name()));
+ let legacy_path = package_root
+ .join("src")
+ .join(format!("{}.rs", name_or_panic(lib)));
if edition == Edition::Edition2015 && legacy_path.exists() {
warnings.push(format!(
"path `{}` was erroneously implicitly accepted for library `{}`,\n\
please rename the file to `src/lib.rs` or set lib.path in Cargo.toml",
legacy_path.display(),
- lib.name()
+ name_or_panic(lib)
));
legacy_path
} else {
anyhow::bail!(
"can't find library `{}`, \
rename file to `src/lib.rs` or specify lib.path",
- lib.name()
+ name_or_panic(lib)
)
}
}
@@ -216,7 +219,7 @@ fn clean_lib(
{
anyhow::bail!(format!(
"library `{}` cannot set the crate type of both `dylib` and `cdylib`",
- lib.name()
+ name_or_panic(lib)
));
}
(Some(kinds), _, _) if kinds.contains(&"proc-macro".to_string()) => {
@@ -224,12 +227,12 @@ fn clean_lib(
// This is a warning to retain backwards compatibility.
warnings.push(format!(
"proc-macro library `{}` should not specify `plugin = true`",
- lib.name()
+ name_or_panic(lib)
));
}
warnings.push(format!(
"library `{}` should only specify `proc-macro = true` instead of setting `crate-type`",
- lib.name()
+ name_or_panic(lib)
));
if kinds.len() > 1 {
anyhow::bail!("cannot mix `proc-macro` crate type with others");
@@ -245,7 +248,7 @@ fn clean_lib(
(None, _, _) => vec![CrateType::Lib],
};
- let mut target = Target::lib_target(&lib.name(), crate_types, path, edition);
+ let mut target = Target::lib_target(name_or_panic(lib), crate_types, path, edition);
configure(lib, &mut target)?;
Ok(Some(target))
}
@@ -285,7 +288,7 @@ fn clean_bins(
validate_target_name(bin, "binary", "bin", warnings)?;
- let name = bin.name();
+ let name = name_or_panic(bin).to_owned();
if let Some(crate_types) = bin.crate_types() {
if !crate_types.is_empty() {
@@ -309,7 +312,7 @@ fn clean_bins(
if restricted_names::is_conflicting_artifact_name(&name) {
anyhow::bail!(
"the binary target name `{}` is forbidden, \
- it conflicts with with cargo's build directory names",
+ it conflicts with cargo's build directory names",
name
)
}
@@ -320,12 +323,12 @@ fn clean_bins(
let mut result = Vec::new();
for bin in &bins {
let path = target_path(bin, &inferred, "bin", package_root, edition, &mut |_| {
- if let Some(legacy_path) = legacy_bin_path(package_root, &bin.name(), has_lib) {
+ if let Some(legacy_path) = legacy_bin_path(package_root, name_or_panic(bin), has_lib) {
warnings.push(format!(
"path `{}` was erroneously implicitly accepted for binary `{}`,\n\
please set bin.path in Cargo.toml",
legacy_path.display(),
- bin.name()
+ name_or_panic(bin)
));
Some(legacy_path)
} else {
@@ -338,7 +341,7 @@ fn clean_bins(
};
let mut target = Target::bin_target(
- &bin.name(),
+ name_or_panic(bin),
bin.filename.clone(),
path,
bin.required_features.clone(),
@@ -398,14 +401,14 @@ fn clean_examples(
let mut result = Vec::new();
for (path, toml) in targets {
- toml.validate_crate_types("example", warnings);
+ validate_crate_types(&toml, "example", warnings);
let crate_types = match toml.crate_types() {
Some(kinds) => kinds.iter().map(|s| s.into()).collect(),
None => Vec::new(),
};
let mut target = Target::example_target(
- &toml.name(),
+ name_or_panic(&toml),
crate_types,
path,
toml.required_features.clone(),
@@ -443,8 +446,12 @@ fn clean_tests(
let mut result = Vec::new();
for (path, toml) in targets {
- let mut target =
- Target::test_target(&toml.name(), path, toml.required_features.clone(), edition);
+ let mut target = Target::test_target(
+ name_or_panic(&toml),
+ path,
+ toml.required_features.clone(),
+ edition,
+ );
configure(&toml, &mut target)?;
result.push(target);
}
@@ -464,14 +471,14 @@ fn clean_benches(
let targets = {
let mut legacy_bench_path = |bench: &TomlTarget| {
let legacy_path = package_root.join("src").join("bench.rs");
- if !(bench.name() == "bench" && legacy_path.exists()) {
+ if !(name_or_panic(bench) == "bench" && legacy_path.exists()) {
return None;
}
legacy_warnings.push(format!(
"path `{}` was erroneously implicitly accepted for benchmark `{}`,\n\
please set bench.path in Cargo.toml",
legacy_path.display(),
- bench.name()
+ name_or_panic(bench)
));
Some(legacy_path)
};
@@ -497,8 +504,12 @@ fn clean_benches(
let mut result = Vec::new();
for (path, toml) in targets {
- let mut target =
- Target::bench_target(&toml.name(), path, toml.required_features.clone(), edition);
+ let mut target = Target::bench_target(
+ name_or_panic(&toml),
+ path,
+ toml.required_features.clone(),
+ edition,
+ );
configure(&toml, &mut target)?;
result.push(target);
}
@@ -784,8 +795,8 @@ fn validate_target_name(
/// Will check a list of toml targets, and make sure the target names are unique within a vector.
fn validate_unique_names(targets: &[TomlTarget], target_kind: &str) -> CargoResult<()> {
let mut seen = HashSet::new();
- for name in targets.iter().map(|e| e.name()) {
- if !seen.insert(name.clone()) {
+ for name in targets.iter().map(|e| name_or_panic(e)) {
+ if !seen.insert(name) {
anyhow::bail!(
"found duplicate {target_kind} name {name}, \
but all {target_kind} targets must have a unique name",
@@ -875,7 +886,7 @@ fn target_path_not_found_error_message(
return [target_path_file, target_path_subdir];
}
- let target_name = target.name();
+ let target_name = name_or_panic(target);
let commonly_wrong_paths = possible_target_paths(&target_name, target_kind, true);
let possible_paths = possible_target_paths(&target_name, target_kind, false);
let existing_wrong_path_index = match (
@@ -922,7 +933,7 @@ fn target_path(
// Should we verify that this path exists here?
return Ok(package_root.join(&path.0));
}
- let name = target.name();
+ let name = name_or_panic(target).to_owned();
let mut matching = inferred
.iter()
@@ -955,7 +966,7 @@ fn target_path(
"\
cannot infer path for `{}` {}
Cargo doesn't know which to use because multiple target files found at `{}` and `{}`.",
- target.name(),
+ name_or_panic(target),
target_kind,
p0.strip_prefix(package_root).unwrap_or(&p0).display(),
p1.strip_prefix(package_root).unwrap_or(&p1).display(),
@@ -964,3 +975,52 @@ Cargo doesn't know which to use because multiple target files found at `{}` and
(None, Some(_)) => unreachable!(),
}
}
+
+/// Returns the path to the build script if one exists for this crate.
+fn maybe_custom_build(build: &Option<StringOrBool>, package_root: &Path) -> Option<PathBuf> {
+ let build_rs = package_root.join("build.rs");
+ match *build {
+ // Explicitly no build script.
+ Some(StringOrBool::Bool(false)) => None,
+ Some(StringOrBool::Bool(true)) => Some(build_rs),
+ Some(StringOrBool::String(ref s)) => Some(PathBuf::from(s)),
+ None => {
+ // If there is a `build.rs` file next to the `Cargo.toml`, assume it is
+ // a build script.
+ if build_rs.is_file() {
+ Some(build_rs)
+ } else {
+ None
+ }
+ }
+ }
+}
+
+fn name_or_panic(target: &TomlTarget) -> &str {
+ target
+ .name
+ .as_deref()
+ .unwrap_or_else(|| panic!("target name is required"))
+}
+
+fn validate_proc_macro(target: &TomlTarget, kind: &str, warnings: &mut Vec<String>) {
+ if target.proc_macro_raw.is_some() && target.proc_macro_raw2.is_some() {
+ warn_on_deprecated(
+ "proc-macro",
+ name_or_panic(target),
+ format!("{kind} target").as_str(),
+ warnings,
+ );
+ }
+}
+
+fn validate_crate_types(target: &TomlTarget, kind: &str, warnings: &mut Vec<String>) {
+ if target.crate_type.is_some() && target.crate_type2.is_some() {
+ warn_on_deprecated(
+ "crate-type",
+ name_or_panic(target),
+ format!("{kind} target").as_str(),
+ warnings,
+ );
+ }
+}
diff --git a/src/tools/cargo/src/cargo/util/toml_mut/dependency.rs b/src/tools/cargo/src/cargo/util/toml_mut/dependency.rs
index 2f39b7ab4..88298fa8d 100644
--- a/src/tools/cargo/src/cargo/util/toml_mut/dependency.rs
+++ b/src/tools/cargo/src/cargo/util/toml_mut/dependency.rs
@@ -464,7 +464,7 @@ impl Dependency {
} else if let Some(table) = item.as_table_like_mut() {
match &self.source {
Some(Source::Registry(src)) => {
- table.insert("version", toml_edit::value(src.version.as_str()));
+ overwrite_value(table, "version", src.version.as_str());
for key in ["path", "git", "branch", "tag", "rev", "workspace"] {
table.remove(key);
@@ -472,9 +472,9 @@ impl Dependency {
}
Some(Source::Path(src)) => {
let relpath = path_field(crate_root, &src.path);
- table.insert("path", toml_edit::value(relpath));
+ overwrite_value(table, "path", relpath);
if let Some(r) = src.version.as_deref() {
- table.insert("version", toml_edit::value(r));
+ overwrite_value(table, "version", r);
} else {
table.remove("version");
}
@@ -484,24 +484,24 @@ impl Dependency {
}
}
Some(Source::Git(src)) => {
- table.insert("git", toml_edit::value(src.git.as_str()));
+ overwrite_value(table, "git", src.git.as_str());
if let Some(branch) = src.branch.as_deref() {
- table.insert("branch", toml_edit::value(branch));
+ overwrite_value(table, "branch", branch);
} else {
table.remove("branch");
}
if let Some(tag) = src.tag.as_deref() {
- table.insert("tag", toml_edit::value(tag));
+ overwrite_value(table, "tag", tag);
} else {
table.remove("tag");
}
if let Some(rev) = src.rev.as_deref() {
- table.insert("rev", toml_edit::value(rev));
+ overwrite_value(table, "rev", rev);
} else {
table.remove("rev");
}
if let Some(r) = src.version.as_deref() {
- table.insert("version", toml_edit::value(r));
+ overwrite_value(table, "version", r);
} else {
table.remove("version");
}
@@ -511,7 +511,7 @@ impl Dependency {
}
}
Some(Source::Workspace(_)) => {
- table.insert("workspace", toml_edit::value(true));
+ overwrite_value(table, "workspace", true);
table.set_dotted(true);
key.fmt();
for key in [
@@ -533,7 +533,7 @@ impl Dependency {
}
if table.contains_key("version") {
if let Some(r) = self.registry.as_deref() {
- table.insert("registry", toml_edit::value(r));
+ overwrite_value(table, "registry", r);
} else {
table.remove("registry");
}
@@ -542,11 +542,11 @@ impl Dependency {
}
if self.rename.is_some() {
- table.insert("package", toml_edit::value(self.name.as_str()));
+ overwrite_value(table, "package", self.name.as_str());
}
match self.default_features {
Some(v) => {
- table.insert("default-features", toml_edit::value(v));
+ overwrite_value(table, "default-features", v);
}
None => {
table.remove("default-features");
@@ -564,29 +564,40 @@ impl Dependency {
})
.unwrap_or_default();
features.extend(new_features.iter().map(|s| s.as_str()));
- let features = toml_edit::value(features.into_iter().collect::<toml_edit::Value>());
+ let features = features.into_iter().collect::<toml_edit::Value>();
table.set_dotted(false);
- table.insert("features", features);
+ overwrite_value(table, "features", features);
} else {
table.remove("features");
}
match self.optional {
Some(v) => {
table.set_dotted(false);
- table.insert("optional", toml_edit::value(v));
+ overwrite_value(table, "optional", v);
}
None => {
table.remove("optional");
}
}
-
- table.fmt();
} else {
unreachable!("Invalid dependency type: {}", item.type_name());
}
}
}
+fn overwrite_value(
+ table: &mut dyn toml_edit::TableLike,
+ key: &str,
+ value: impl Into<toml_edit::Value>,
+) {
+ let mut value = value.into();
+ let existing = table.entry(key).or_insert_with(|| Default::default());
+ if let Some(existing_value) = existing.as_value() {
+ *value.decor_mut() = existing_value.decor().clone();
+ }
+ *existing = toml_edit::Item::Value(value);
+}
+
fn invalid_type(dep: &str, key: &str, actual: &str, expected: &str) -> anyhow::Error {
anyhow::format_err!("Found {actual} for {key} when {expected} was expected for {dep}")
}
diff --git a/src/tools/cargo/src/cargo/util/toml_mut/manifest.rs b/src/tools/cargo/src/cargo/util/toml_mut/manifest.rs
index 5529b8029..e859af215 100644
--- a/src/tools/cargo/src/cargo/util/toml_mut/manifest.rs
+++ b/src/tools/cargo/src/cargo/util/toml_mut/manifest.rs
@@ -296,7 +296,7 @@ impl LocalManifest {
let s = self.manifest.data.to_string();
let new_contents_bytes = s.as_bytes();
- cargo_util::paths::write(&self.path, new_contents_bytes)
+ cargo_util::paths::write_atomic(&self.path, new_contents_bytes)
}
/// Lookup a dependency.
@@ -349,13 +349,16 @@ impl LocalManifest {
.get_key_value_mut(dep_key)
{
dep.update_toml(&crate_root, &mut dep_key, dep_item);
+ if let Some(table) = dep_item.as_inline_table_mut() {
+ // So long as we don't have `Cargo.toml` auto-formatting and inline-tables can only
+ // be on one line, there isn't really much in the way of interesting formatting to
+ // include (no comments), so let's just wipe it clean
+ table.fmt();
+ }
} else {
let new_dependency = dep.to_toml(&crate_root);
table[dep_key] = new_dependency;
}
- if let Some(t) = table.as_inline_table_mut() {
- t.fmt()
- }
Ok(())
}
@@ -364,17 +367,31 @@ impl LocalManifest {
pub fn remove_from_table(&mut self, table_path: &[String], name: &str) -> CargoResult<()> {
let parent_table = self.get_table_mut(table_path)?;
- let dep = parent_table
- .get_mut(name)
- .filter(|t| !t.is_none())
- .ok_or_else(|| non_existent_dependency_err(name, table_path.join(".")))?;
+ match parent_table.get_mut(name).filter(|t| !t.is_none()) {
+ Some(dep) => {
+ // remove the dependency
+ *dep = toml_edit::Item::None;
- // remove the dependency
- *dep = toml_edit::Item::None;
+ // remove table if empty
+ if parent_table.as_table_like().unwrap().is_empty() {
+ *parent_table = toml_edit::Item::None;
+ }
+ }
+ None => {
+ // Search in other tables.
+ let sections = self.get_sections();
+ let found_table_path = sections.iter().find_map(|(t, i)| {
+ let table_path: Vec<String> =
+ t.to_table().iter().map(|s| s.to_string()).collect();
+ i.get(name).is_some().then(|| table_path.join("."))
+ });
- // remove table if empty
- if parent_table.as_table_like().unwrap().is_empty() {
- *parent_table = toml_edit::Item::None;
+ return Err(non_existent_dependency_err(
+ name,
+ table_path.join("."),
+ found_table_path,
+ ));
+ }
}
Ok(())
@@ -494,12 +511,7 @@ fn fix_feature_activations(
// Remove found idx in revers order so we don't invalidate the idx.
for idx in remove_list.iter().rev() {
- feature_values.remove(*idx);
- }
- if !remove_list.is_empty() {
- // HACK: Instead of cleaning up the users formatting from having removed a feature, we just
- // re-format the whole feature list
- feature_values.fmt();
+ remove_array_index(feature_values, *idx);
}
if status == DependencyStatus::Required {
@@ -539,7 +551,53 @@ fn non_existent_table_err(table: impl std::fmt::Display) -> anyhow::Error {
fn non_existent_dependency_err(
name: impl std::fmt::Display,
- table: impl std::fmt::Display,
+ search_table: impl std::fmt::Display,
+ found_table: Option<impl std::fmt::Display>,
) -> anyhow::Error {
- anyhow::format_err!("the dependency `{name}` could not be found in `{table}`.")
+ let mut msg = format!("the dependency `{name}` could not be found in `{search_table}`");
+ if let Some(found_table) = found_table {
+ msg.push_str(&format!("; it is present in `{found_table}`",));
+ }
+ anyhow::format_err!(msg)
+}
+
+fn remove_array_index(array: &mut toml_edit::Array, index: usize) {
+ let value = array.remove(index);
+
+ // Captures all lines before leading whitespace
+ let prefix_lines = value
+ .decor()
+ .prefix()
+ .and_then(|p| p.as_str().expect("spans removed").rsplit_once('\n'))
+ .map(|(lines, _current)| lines);
+ // Captures all lines after trailing whitespace, before the next comma
+ let suffix_lines = value
+ .decor()
+ .suffix()
+ .and_then(|p| p.as_str().expect("spans removed").split_once('\n'))
+ .map(|(_current, lines)| lines);
+ let mut merged_lines = String::new();
+ if let Some(prefix_lines) = prefix_lines {
+ merged_lines.push_str(prefix_lines);
+ merged_lines.push('\n');
+ }
+ if let Some(suffix_lines) = suffix_lines {
+ merged_lines.push_str(suffix_lines);
+ merged_lines.push('\n');
+ }
+
+ let next_index = index; // Since `index` was removed, that effectively auto-advances us
+ if let Some(next) = array.get_mut(next_index) {
+ let next_decor = next.decor_mut();
+ let next_prefix = next_decor
+ .prefix()
+ .map(|s| s.as_str().expect("spans removed"))
+ .unwrap_or_default();
+ merged_lines.push_str(next_prefix);
+ next_decor.set_prefix(merged_lines);
+ } else {
+ let trailing = array.trailing().as_str().expect("spans removed");
+ merged_lines.push_str(trailing);
+ array.set_trailing(merged_lines);
+ }
}
diff --git a/src/tools/cargo/src/cargo/util/toml_mut/mod.rs b/src/tools/cargo/src/cargo/util/toml_mut/mod.rs
index bdd70e8e6..cb5d3aaf2 100644
--- a/src/tools/cargo/src/cargo/util/toml_mut/mod.rs
+++ b/src/tools/cargo/src/cargo/util/toml_mut/mod.rs
@@ -11,3 +11,19 @@
pub mod dependency;
pub mod manifest;
+
+// Based on Iterator::is_sorted from nightly std; remove in favor of that when stabilized.
+pub fn is_sorted(mut it: impl Iterator<Item = impl PartialOrd>) -> bool {
+ let Some(mut last) = it.next() else {
+ return true;
+ };
+
+ for curr in it {
+ if curr < last {
+ return false;
+ }
+ last = curr;
+ }
+
+ true
+}
diff --git a/src/tools/cargo/src/cargo/util/workspace.rs b/src/tools/cargo/src/cargo/util/workspace.rs
index e8317f101..a2e0fff50 100644
--- a/src/tools/cargo/src/cargo/util/workspace.rs
+++ b/src/tools/cargo/src/cargo/util/workspace.rs
@@ -87,11 +87,11 @@ pub fn print_available_binaries(ws: &Workspace<'_>, options: &CompileOptions) ->
}
pub fn print_available_benches(ws: &Workspace<'_>, options: &CompileOptions) -> CargoResult<()> {
- print_available_targets(Target::is_bench, ws, options, "--bench", "benches")
+ print_available_targets(Target::is_bench, ws, options, "--bench", "bench targets")
}
pub fn print_available_tests(ws: &Workspace<'_>, options: &CompileOptions) -> CargoResult<()> {
- print_available_targets(Target::is_test, ws, options, "--test", "tests")
+ print_available_targets(Target::is_test, ws, options, "--test", "test targets")
}
/// The path that we pass to rustc is actually fairly important because it will
diff --git a/src/tools/cargo/src/cargo/util_semver.rs b/src/tools/cargo/src/cargo/util_semver.rs
new file mode 100644
index 000000000..a84c9ee58
--- /dev/null
+++ b/src/tools/cargo/src/cargo/util_semver.rs
@@ -0,0 +1,195 @@
+use std::fmt::{self, Display};
+
+use semver::{Comparator, Op, Version, VersionReq};
+use serde_untagged::UntaggedEnumVisitor;
+
+pub trait VersionExt {
+ fn is_prerelease(&self) -> bool;
+
+ fn to_exact_req(&self) -> VersionReq;
+}
+
+impl VersionExt for Version {
+ fn is_prerelease(&self) -> bool {
+ !self.pre.is_empty()
+ }
+
+ fn to_exact_req(&self) -> VersionReq {
+ VersionReq {
+ comparators: vec![Comparator {
+ op: Op::Exact,
+ major: self.major,
+ minor: Some(self.minor),
+ patch: Some(self.patch),
+ pre: self.pre.clone(),
+ }],
+ }
+ }
+}
+
+#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Debug)]
+pub struct PartialVersion {
+ pub major: u64,
+ pub minor: Option<u64>,
+ pub patch: Option<u64>,
+ pub pre: Option<semver::Prerelease>,
+ pub build: Option<semver::BuildMetadata>,
+}
+
+impl PartialVersion {
+ pub fn to_version(&self) -> Option<Version> {
+ Some(Version {
+ major: self.major,
+ minor: self.minor?,
+ patch: self.patch?,
+ pre: self.pre.clone().unwrap_or_default(),
+ build: self.build.clone().unwrap_or_default(),
+ })
+ }
+
+ pub fn to_caret_req(&self) -> VersionReq {
+ VersionReq {
+ comparators: vec![Comparator {
+ op: semver::Op::Caret,
+ major: self.major,
+ minor: self.minor,
+ patch: self.patch,
+ pre: self.pre.as_ref().cloned().unwrap_or_default(),
+ }],
+ }
+ }
+
+ /// Check if this matches a version, including build metadata
+ ///
+ /// Build metadata does not affect version precedence but may be necessary for uniquely
+ /// identifying a package.
+ pub fn matches(&self, version: &Version) -> bool {
+ if !version.pre.is_empty() && self.pre.is_none() {
+ // Pre-release versions must be explicitly opted into, if for no other reason than to
+ // give us room to figure out and define the semantics
+ return false;
+ }
+ self.major == version.major
+ && self.minor.map(|f| f == version.minor).unwrap_or(true)
+ && self.patch.map(|f| f == version.patch).unwrap_or(true)
+ && self.pre.as_ref().map(|f| f == &version.pre).unwrap_or(true)
+ && self
+ .build
+ .as_ref()
+ .map(|f| f == &version.build)
+ .unwrap_or(true)
+ }
+}
+
+impl From<semver::Version> for PartialVersion {
+ fn from(ver: semver::Version) -> Self {
+ let pre = if ver.pre.is_empty() {
+ None
+ } else {
+ Some(ver.pre)
+ };
+ let build = if ver.build.is_empty() {
+ None
+ } else {
+ Some(ver.build)
+ };
+ Self {
+ major: ver.major,
+ minor: Some(ver.minor),
+ patch: Some(ver.patch),
+ pre,
+ build,
+ }
+ }
+}
+
+impl std::str::FromStr for PartialVersion {
+ type Err = anyhow::Error;
+
+ fn from_str(value: &str) -> Result<Self, Self::Err> {
+ if is_req(value) {
+ anyhow::bail!("unexpected version requirement, expected a version like \"1.32\"")
+ }
+ match semver::Version::parse(value) {
+ Ok(ver) => Ok(ver.into()),
+ Err(_) => {
+ // HACK: Leverage `VersionReq` for partial version parsing
+ let mut version_req = match semver::VersionReq::parse(value) {
+ Ok(req) => req,
+ Err(_) if value.contains('-') => {
+ anyhow::bail!(
+ "unexpected prerelease field, expected a version like \"1.32\""
+ )
+ }
+ Err(_) if value.contains('+') => {
+ anyhow::bail!("unexpected build field, expected a version like \"1.32\"")
+ }
+ Err(_) => anyhow::bail!("expected a version like \"1.32\""),
+ };
+ assert_eq!(version_req.comparators.len(), 1, "guaranteed by is_req");
+ let comp = version_req.comparators.pop().unwrap();
+ assert_eq!(comp.op, semver::Op::Caret, "guaranteed by is_req");
+ let pre = if comp.pre.is_empty() {
+ None
+ } else {
+ Some(comp.pre)
+ };
+ Ok(Self {
+ major: comp.major,
+ minor: comp.minor,
+ patch: comp.patch,
+ pre,
+ build: None,
+ })
+ }
+ }
+ }
+}
+
+impl Display for PartialVersion {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ let major = self.major;
+ write!(f, "{major}")?;
+ if let Some(minor) = self.minor {
+ write!(f, ".{minor}")?;
+ }
+ if let Some(patch) = self.patch {
+ write!(f, ".{patch}")?;
+ }
+ if let Some(pre) = self.pre.as_ref() {
+ write!(f, "-{pre}")?;
+ }
+ if let Some(build) = self.build.as_ref() {
+ write!(f, "+{build}")?;
+ }
+ Ok(())
+ }
+}
+
+impl serde::Serialize for PartialVersion {
+ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+ where
+ S: serde::Serializer,
+ {
+ serializer.collect_str(self)
+ }
+}
+
+impl<'de> serde::Deserialize<'de> for PartialVersion {
+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+ where
+ D: serde::Deserializer<'de>,
+ {
+ UntaggedEnumVisitor::new()
+ .expecting("SemVer version")
+ .string(|value| value.parse().map_err(serde::de::Error::custom))
+ .deserialize(deserializer)
+ }
+}
+
+fn is_req(value: &str) -> bool {
+ let Some(first) = value.chars().next() else {
+ return false;
+ };
+ "<>=^~".contains(first) || value.contains('*') || value.contains(',')
+}
diff --git a/src/tools/cargo/src/doc/contrib/book.toml b/src/tools/cargo/src/doc/contrib/book.toml
index 628179c0d..e322fd8f3 100644
--- a/src/tools/cargo/src/doc/contrib/book.toml
+++ b/src/tools/cargo/src/doc/contrib/book.toml
@@ -5,6 +5,8 @@ authors = ["Eric Huss"]
[output.html]
curly-quotes = true # Enable smart-punctuation feature for more than quotes.
git-repository-url = "https://github.com/rust-lang/cargo/tree/master/src/doc/contrib/src"
+edit-url-template = "https://github.com/rust-lang/cargo/edit/master/src/doc/contrib/{path}"
+search.use-boolean-and = true
[output.html.redirect]
"/apidoc/cargo/index.html" = "https://doc.rust-lang.org/nightly/nightly-rustc/cargo/"
diff --git a/src/tools/cargo/src/doc/contrib/src/SUMMARY.md b/src/tools/cargo/src/doc/contrib/src/SUMMARY.md
index f8796b3f3..e97028eb5 100644
--- a/src/tools/cargo/src/doc/contrib/src/SUMMARY.md
+++ b/src/tools/cargo/src/doc/contrib/src/SUMMARY.md
@@ -11,9 +11,11 @@
- [Design Principles](./design.md)
- [Implementing a Change](./implementation/index.md)
- [Architecture](./implementation/architecture.md)
+ - [New packages](./implementation/packages.md)
- [New subcommands](./implementation/subcommands.md)
- [Console Output](./implementation/console.md)
- [Filesystem](./implementation/filesystem.md)
+ - [Formatting](./implementation/formatting.md)
- [Debugging](./implementation/debugging.md)
- [Tests](./tests/index.md)
- [Running Tests](./tests/running.md)
diff --git a/src/tools/cargo/src/doc/contrib/src/implementation/formatting.md b/src/tools/cargo/src/doc/contrib/src/implementation/formatting.md
new file mode 100644
index 000000000..91321f1a5
--- /dev/null
+++ b/src/tools/cargo/src/doc/contrib/src/implementation/formatting.md
@@ -0,0 +1,17 @@
+# Formatting
+
+When modifying user files, like `Cargo.toml`, we should not change other
+sections of the file,
+preserving the general formatting.
+This includes the table, inline-table, or array that a field is being edited in.
+
+When adding new entries, they do not need to match the canonical style of the
+document but can use the default formatting.
+If the entry is already sorted, preserving the sort order is preferred.
+
+When removing entries,
+comments on the same line should be removed but comments on following lines
+should be preserved.
+
+Inconsistencies in style after making a change are left to the user and their
+preferred auto-formatter.
diff --git a/src/tools/cargo/src/doc/contrib/src/implementation/packages.md b/src/tools/cargo/src/doc/contrib/src/implementation/packages.md
new file mode 100644
index 000000000..e7d965eca
--- /dev/null
+++ b/src/tools/cargo/src/doc/contrib/src/implementation/packages.md
@@ -0,0 +1,52 @@
+# New Packages
+
+This chapter sketches out how to add a new package to the cargo workspace.
+
+## Steps
+
+Choose the relevant parent directory
+- `credential/` for credential-process related packages
+- `benches/` for benchmarking of cargo itself
+- `crates/` for everything else
+
+Run `cargo new <name>`
+- `<name>`:
+ - We tend to use `-` over `_`
+ - For internal APIs, to avoid collisions with third-party subcommands, we can use the `cargo-util-` prefix
+ - For xtasks, we use the `xtask-` prefix
+- `package.rust-version`
+ - Internal packages tend to have a policy of "latest" with a [`# MSRV:1` comment](#msrv-policy)
+ - Ecosystem packages tend to have a policy of "N-2" with a [`# MSRV:3` comment](#msrv-policy)
+ - If the right choice is inherited from the workspace, feel free to keep it that way
+- If running without [cargo new automatically adding to workspace](https://github.com/rust-lang/cargo/pull/12779), add it as a workspace member if not already captured by a glob
+
+If its an xtask,
+- Add it to `.cargo/config.toml`s `[alias]` table
+- Mark `package.publish = false`
+
+If needed to be published with `cargo`,
+add the package to `publish.py` in the repo root,
+in dependency order.
+
+Note: by adding the package to the workspace, you automatically get
+- CI running `cargo test`
+- CI verifying MSRV
+- CI checking for `cargo doc` warnings
+
+## MSRV Policy
+
+Our MSRV policies are
+- Internal packages: support latest version
+- Ecosystem packages: support latest 3 versions
+
+We proactively update the MSRV
+- So contributors don't shy away from using newer features, either assuming they
+ can't ask or feeling like they have to have a justification when asking
+- To avoid a de facto MSRV developing from staying on a version for a long
+ period of time, leaving users unhappy when their expectations aren't met
+
+To proactively update the MSRV, we use [RenovateBot](https://docs.renovatebot.com/)
+with the configuration file in `.github/renovatebot.json5`.
+To know what MSRV policy to use,
+it looks for comments of the form `# MSRV:N`,
+where `N` is the number of supported rust versions.
diff --git a/src/tools/cargo/src/doc/contrib/src/process/index.md b/src/tools/cargo/src/doc/contrib/src/process/index.md
index c9dae918c..b5cfa5e46 100644
--- a/src/tools/cargo/src/doc/contrib/src/process/index.md
+++ b/src/tools/cargo/src/doc/contrib/src/process/index.md
@@ -8,11 +8,6 @@ process.
Please read the guidelines below before working on an issue or new feature.
-**Due to limited review capacity, the Cargo team is not accepting new features
-or major changes at this time. Please consult with the team before opening a
-new PR. Only issues that have been explicitly marked as accepted will be
-reviewed.**
-
[Working on Cargo]: working-on-cargo.md
## Mentorship
@@ -30,8 +25,8 @@ an overview of the things the team is interested in and thinking about.
The [RFC Project Board] is used for tracking [RFCs].
[the 2020 roadmap]: https://blog.rust-lang.org/inside-rust/2020/01/10/cargo-in-2020.html
-[Roadmap Project Board]: https://github.com/rust-lang/cargo/projects/1
-[RFC Project Board]: https://github.com/rust-lang/cargo/projects/2
+[Roadmap Project Board]: https://github.com/orgs/rust-lang/projects/37
+[RFC Project Board]: https://github.com/orgs/rust-lang/projects/36
[RFCs]: https://github.com/rust-lang/rfcs/
## Working on issues
diff --git a/src/tools/cargo/src/doc/contrib/src/process/working-on-cargo.md b/src/tools/cargo/src/doc/contrib/src/process/working-on-cargo.md
index 1567197c4..68f20db38 100644
--- a/src/tools/cargo/src/doc/contrib/src/process/working-on-cargo.md
+++ b/src/tools/cargo/src/doc/contrib/src/process/working-on-cargo.md
@@ -17,10 +17,12 @@ We encourage people to discuss their design before hacking on code. This gives
the Cargo team a chance to know your idea more. Sometimes after a discussion,
we even find a way to solve the problem without coding! Typically, you
[file an issue] or start a thread on the [internals forum] before submitting a
-pull request. Please read [the process] of how features and bugs are managed in
-Cargo.
+pull request.
-## Checkout out the source
+Please read [the process] of how features and bugs are managed in Cargo.
+**Only issues that have been explicitly marked as [accepted] will be reviewed.**
+
+## Checkout the source
We use the "fork and pull" model [described here][development-models], where
contributors push changes to their personal fork and [create pull requests] to
@@ -170,3 +172,4 @@ more information on how Cargo releases are made.
[internals forum]: https://internals.rust-lang.org/c/tools-and-infrastructure/cargo
[file an issue]: https://github.com/rust-lang/cargo/issues
[the process]: index.md
+[accepted]: https://github.com/rust-lang/cargo/issues?q=is%3Aissue+is%3Aopen+label%3AS-accepted
diff --git a/src/tools/cargo/src/doc/man/cargo-add.md b/src/tools/cargo/src/doc/man/cargo-add.md
index 21c222a39..d3a62900a 100644
--- a/src/tools/cargo/src/doc/man/cargo-add.md
+++ b/src/tools/cargo/src/doc/man/cargo-add.md
@@ -35,7 +35,7 @@ When you add a package that is already present, the existing entry will be updat
Upon successful invocation, the enabled (`+`) and disabled (`-`) [features] of the specified
dependency will be listed in the command's output.
-[features]: ../reference/features.md
+[features]: ../reference/features.html
## OPTIONS
diff --git a/src/tools/cargo/src/doc/man/cargo-bench.md b/src/tools/cargo/src/doc/man/cargo-bench.md
index a2a602847..5b1c6ba78 100644
--- a/src/tools/cargo/src/doc/man/cargo-bench.md
+++ b/src/tools/cargo/src/doc/man/cargo-bench.md
@@ -34,7 +34,7 @@ Benchmarks are built with the `--test` option to `rustc` which creates a
special executable by linking your code with libtest. The executable
automatically runs all functions annotated with the `#[bench]` attribute.
Cargo passes the `--bench` flag to the test harness to tell it to run
-only benchmarks.
+only benchmarks, regardless of whether the harness is libtest or a custom harness.
The libtest harness may be disabled by setting `harness = false` in the target
manifest settings, in which case your code will need to provide its own `main`
diff --git a/src/tools/cargo/src/doc/man/cargo-install.md b/src/tools/cargo/src/doc/man/cargo-install.md
index 5431bdb79..8c520db0a 100644
--- a/src/tools/cargo/src/doc/man/cargo-install.md
+++ b/src/tools/cargo/src/doc/man/cargo-install.md
@@ -90,7 +90,7 @@ will be used, beginning discovery at `$PATH/.cargo/config.toml`.
{{#option "`--vers` _version_" "`--version` _version_" }}
Specify a version to install. This may be a [version
-requirement](../reference/specifying-dependencies.md), like `~1.2`, to have Cargo
+requirement](../reference/specifying-dependencies.html), like `~1.2`, to have Cargo
select the newest version from the given requirement. If the version does not
have a requirement operator (such as `^` or `~`), then it must be in the form
_MAJOR.MINOR.PATCH_, and will install exactly that version; it is *not*
diff --git a/src/tools/cargo/src/doc/man/cargo-login.md b/src/tools/cargo/src/doc/man/cargo-login.md
index 197636da8..46681f7ef 100644
--- a/src/tools/cargo/src/doc/man/cargo-login.md
+++ b/src/tools/cargo/src/doc/man/cargo-login.md
@@ -6,7 +6,7 @@ cargo-login --- Log in to a registry
## SYNOPSIS
-`cargo login` [_options_] [_token_] -- [_args_]
+`cargo login` [_options_] [_token_] [`--` _args_]
## DESCRIPTION
@@ -14,6 +14,8 @@ This command will run a credential provider to save a token so that commands
that require authentication, such as {{man "cargo-publish" 1}}, will be
automatically authenticated.
+All the arguments following the two dashes (`--`) are passed to the credential provider.
+
For the default `cargo:token` credential provider, the token is saved
in `$CARGO_HOME/credentials.toml`. `CARGO_HOME` defaults to `.cargo`
in your home directory.
diff --git a/src/tools/cargo/src/doc/man/cargo-rustc.md b/src/tools/cargo/src/doc/man/cargo-rustc.md
index 279c6dbd1..e60b12e8d 100644
--- a/src/tools/cargo/src/doc/man/cargo-rustc.md
+++ b/src/tools/cargo/src/doc/man/cargo-rustc.md
@@ -65,7 +65,7 @@ The `rustc` subcommand will treat the following named profiles with special beha
* `bench` --- Builds in the same was as the {{man "cargo-bench" 1}} command,
similar to the `test` profile.
-See the [the reference](../reference/profiles.html) for more details on profiles.
+See [the reference](../reference/profiles.html) for more details on profiles.
{{/option}}
{{> options-ignore-rust-version }}
diff --git a/src/tools/cargo/src/doc/man/cargo-vendor.md b/src/tools/cargo/src/doc/man/cargo-vendor.md
index b30d0d8dd..169e64db2 100644
--- a/src/tools/cargo/src/doc/man/cargo-vendor.md
+++ b/src/tools/cargo/src/doc/man/cargo-vendor.md
@@ -16,8 +16,10 @@ the vendor directory specified by `<path>` will contain all remote sources from
dependencies specified. Additional manifests beyond the default one can be
specified with the `-s` option.
-The `cargo vendor` command will also print out the configuration necessary
-to use the vendored sources, which you will need to add to `.cargo/config.toml`.
+The configuration necessary to use the vendored sources would be printed to
+stdout after `cargo vendor` completes the vendoring process.
+You will need to add or redirect it to your Cargo configuration file,
+which is usually `.cargo/config.toml` locally for the current package.
## OPTIONS
@@ -88,6 +90,10 @@ only a subset of the packages have changed.
cargo vendor -s ../path/to/Cargo.toml
+4. Vendor and redirect the necessary vendor configs to a config file.
+
+ cargo vendor > path/to/my/cargo/config.toml
+
## SEE ALSO
{{man "cargo" 1}}
diff --git a/src/tools/cargo/src/doc/man/generated_txt/cargo-add.txt b/src/tools/cargo/src/doc/man/generated_txt/cargo-add.txt
index 6447499dc..3bd4bd5aa 100644
--- a/src/tools/cargo/src/doc/man/generated_txt/cargo-add.txt
+++ b/src/tools/cargo/src/doc/man/generated_txt/cargo-add.txt
@@ -33,8 +33,8 @@ DESCRIPTION
be updated with the flags specified.
Upon successful invocation, the enabled (+) and disabled (-) features
- <https://doc.rust-lang.org/cargo/reference/features.md> of the specified
- dependency will be listed in the command’s output.
+ <https://doc.rust-lang.org/cargo/reference/features.html> of the
+ specified dependency will be listed in the command’s output.
OPTIONS
Source options
diff --git a/src/tools/cargo/src/doc/man/generated_txt/cargo-bench.txt b/src/tools/cargo/src/doc/man/generated_txt/cargo-bench.txt
index 796fbd11b..a7013a077 100644
--- a/src/tools/cargo/src/doc/man/generated_txt/cargo-bench.txt
+++ b/src/tools/cargo/src/doc/man/generated_txt/cargo-bench.txt
@@ -27,7 +27,8 @@ DESCRIPTION
special executable by linking your code with libtest. The executable
automatically runs all functions annotated with the #[bench] attribute.
Cargo passes the --bench flag to the test harness to tell it to run only
- benchmarks.
+ benchmarks, regardless of whether the harness is libtest or a custom
+ harness.
The libtest harness may be disabled by setting harness = false in the
target manifest settings, in which case your code will need to provide
@@ -235,7 +236,7 @@ OPTIONS
documentation for more details.
--profile name
- Benchmark with the given profile. See the the reference
+ Benchmark with the given profile. See the reference
<https://doc.rust-lang.org/cargo/reference/profiles.html> for more
details on profiles.
diff --git a/src/tools/cargo/src/doc/man/generated_txt/cargo-build.txt b/src/tools/cargo/src/doc/man/generated_txt/cargo-build.txt
index 06a7a6b3c..db39e2011 100644
--- a/src/tools/cargo/src/doc/man/generated_txt/cargo-build.txt
+++ b/src/tools/cargo/src/doc/man/generated_txt/cargo-build.txt
@@ -157,7 +157,7 @@ OPTIONS
--profile option for choosing a specific profile by name.
--profile name
- Build with the given profile. See the the reference
+ Build with the given profile. See the reference
<https://doc.rust-lang.org/cargo/reference/profiles.html> for more
details on profiles.
diff --git a/src/tools/cargo/src/doc/man/generated_txt/cargo-check.txt b/src/tools/cargo/src/doc/man/generated_txt/cargo-check.txt
index b447455ee..5a3ea18f4 100644
--- a/src/tools/cargo/src/doc/man/generated_txt/cargo-check.txt
+++ b/src/tools/cargo/src/doc/man/generated_txt/cargo-check.txt
@@ -161,7 +161,7 @@ OPTIONS
the test cfg option. See rustc tests
<https://doc.rust-lang.org/rustc/tests/index.html> for more detail.
- See the the reference
+ See the reference
<https://doc.rust-lang.org/cargo/reference/profiles.html> for more
details on profiles.
diff --git a/src/tools/cargo/src/doc/man/generated_txt/cargo-doc.txt b/src/tools/cargo/src/doc/man/generated_txt/cargo-doc.txt
index 773d600c6..f34e08df9 100644
--- a/src/tools/cargo/src/doc/man/generated_txt/cargo-doc.txt
+++ b/src/tools/cargo/src/doc/man/generated_txt/cargo-doc.txt
@@ -132,7 +132,7 @@ OPTIONS
--profile option for choosing a specific profile by name.
--profile name
- Document with the given profile. See the the reference
+ Document with the given profile. See the reference
<https://doc.rust-lang.org/cargo/reference/profiles.html> for more
details on profiles.
diff --git a/src/tools/cargo/src/doc/man/generated_txt/cargo-fix.txt b/src/tools/cargo/src/doc/man/generated_txt/cargo-fix.txt
index 3e7910ca5..474c2beb3 100644
--- a/src/tools/cargo/src/doc/man/generated_txt/cargo-fix.txt
+++ b/src/tools/cargo/src/doc/man/generated_txt/cargo-fix.txt
@@ -234,7 +234,7 @@ OPTIONS
the test cfg option. See rustc tests
<https://doc.rust-lang.org/rustc/tests/index.html> for more detail.
- See the the reference
+ See the reference
<https://doc.rust-lang.org/cargo/reference/profiles.html> for more
details on profiles.
diff --git a/src/tools/cargo/src/doc/man/generated_txt/cargo-init.txt b/src/tools/cargo/src/doc/man/generated_txt/cargo-init.txt
index 678024881..5c7e386c3 100644
--- a/src/tools/cargo/src/doc/man/generated_txt/cargo-init.txt
+++ b/src/tools/cargo/src/doc/man/generated_txt/cargo-init.txt
@@ -31,7 +31,7 @@ OPTIONS
--edition edition
Specify the Rust edition to use. Default is 2021. Possible values:
- 2015, 2018, 2021
+ 2015, 2018, 2021, 2024
--name name
Set the package name. Defaults to the directory name.
diff --git a/src/tools/cargo/src/doc/man/generated_txt/cargo-install.txt b/src/tools/cargo/src/doc/man/generated_txt/cargo-install.txt
index 02790f1d0..0b4aece70 100644
--- a/src/tools/cargo/src/doc/man/generated_txt/cargo-install.txt
+++ b/src/tools/cargo/src/doc/man/generated_txt/cargo-install.txt
@@ -94,7 +94,7 @@ OPTIONS
Install Options
--vers version, --version version
Specify a version to install. This may be a version requirement
- <https://doc.rust-lang.org/cargo/reference/specifying-dependencies.md>,
+ <https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html>,
like ~1.2, to have Cargo select the newest version from the given
requirement. If the version does not have a requirement operator
(such as ^ or ~), then it must be in the form MAJOR.MINOR.PATCH, and
@@ -212,7 +212,7 @@ OPTIONS
the --profile option for choosing a specific profile by name.
--profile name
- Install with the given profile. See the the reference
+ Install with the given profile. See the reference
<https://doc.rust-lang.org/cargo/reference/profiles.html> for more
details on profiles.
diff --git a/src/tools/cargo/src/doc/man/generated_txt/cargo-login.txt b/src/tools/cargo/src/doc/man/generated_txt/cargo-login.txt
index ae8127fc9..585a7bf2e 100644
--- a/src/tools/cargo/src/doc/man/generated_txt/cargo-login.txt
+++ b/src/tools/cargo/src/doc/man/generated_txt/cargo-login.txt
@@ -4,13 +4,16 @@ NAME
cargo-login — Log in to a registry
SYNOPSIS
- cargo login [options] [token] – [args]
+ cargo login [options] [token] [-- args]
DESCRIPTION
This command will run a credential provider to save a token so that
commands that require authentication, such as cargo-publish(1), will be
automatically authenticated.
+ All the arguments following the two dashes (--) are passed to the
+ credential provider.
+
For the default cargo:token credential provider, the token is saved in
$CARGO_HOME/credentials.toml. CARGO_HOME defaults to .cargo in your home
directory.
diff --git a/src/tools/cargo/src/doc/man/generated_txt/cargo-new.txt b/src/tools/cargo/src/doc/man/generated_txt/cargo-new.txt
index 5d2c61b48..5f5ebbfbd 100644
--- a/src/tools/cargo/src/doc/man/generated_txt/cargo-new.txt
+++ b/src/tools/cargo/src/doc/man/generated_txt/cargo-new.txt
@@ -26,7 +26,7 @@ OPTIONS
--edition edition
Specify the Rust edition to use. Default is 2021. Possible values:
- 2015, 2018, 2021
+ 2015, 2018, 2021, 2024
--name name
Set the package name. Defaults to the directory name.
diff --git a/src/tools/cargo/src/doc/man/generated_txt/cargo-run.txt b/src/tools/cargo/src/doc/man/generated_txt/cargo-run.txt
index 495a08a6c..00e629337 100644
--- a/src/tools/cargo/src/doc/man/generated_txt/cargo-run.txt
+++ b/src/tools/cargo/src/doc/man/generated_txt/cargo-run.txt
@@ -80,7 +80,7 @@ OPTIONS
--profile option for choosing a specific profile by name.
--profile name
- Run with the given profile. See the the reference
+ Run with the given profile. See the reference
<https://doc.rust-lang.org/cargo/reference/profiles.html> for more
details on profiles.
diff --git a/src/tools/cargo/src/doc/man/generated_txt/cargo-rustc.txt b/src/tools/cargo/src/doc/man/generated_txt/cargo-rustc.txt
index af6ad9d59..866361e93 100644
--- a/src/tools/cargo/src/doc/man/generated_txt/cargo-rustc.txt
+++ b/src/tools/cargo/src/doc/man/generated_txt/cargo-rustc.txt
@@ -165,7 +165,7 @@ OPTIONS
o bench — Builds in the same was as the cargo-bench(1) command,
similar to the test profile.
- See the the reference
+ See the reference
<https://doc.rust-lang.org/cargo/reference/profiles.html> for more
details on profiles.
diff --git a/src/tools/cargo/src/doc/man/generated_txt/cargo-rustdoc.txt b/src/tools/cargo/src/doc/man/generated_txt/cargo-rustdoc.txt
index 5ba200644..3237c4cae 100644
--- a/src/tools/cargo/src/doc/man/generated_txt/cargo-rustdoc.txt
+++ b/src/tools/cargo/src/doc/man/generated_txt/cargo-rustdoc.txt
@@ -148,7 +148,7 @@ OPTIONS
--profile option for choosing a specific profile by name.
--profile name
- Document with the given profile. See the the reference
+ Document with the given profile. See the reference
<https://doc.rust-lang.org/cargo/reference/profiles.html> for more
details on profiles.
diff --git a/src/tools/cargo/src/doc/man/generated_txt/cargo-test.txt b/src/tools/cargo/src/doc/man/generated_txt/cargo-test.txt
index b992d0d2f..598c8f9af 100644
--- a/src/tools/cargo/src/doc/man/generated_txt/cargo-test.txt
+++ b/src/tools/cargo/src/doc/man/generated_txt/cargo-test.txt
@@ -262,7 +262,7 @@ OPTIONS
--profile option for choosing a specific profile by name.
--profile name
- Test with the given profile. See the the reference
+ Test with the given profile. See the reference
<https://doc.rust-lang.org/cargo/reference/profiles.html> for more
details on profiles.
diff --git a/src/tools/cargo/src/doc/man/generated_txt/cargo-vendor.txt b/src/tools/cargo/src/doc/man/generated_txt/cargo-vendor.txt
index c325b7534..f0314aedb 100644
--- a/src/tools/cargo/src/doc/man/generated_txt/cargo-vendor.txt
+++ b/src/tools/cargo/src/doc/man/generated_txt/cargo-vendor.txt
@@ -13,9 +13,10 @@ DESCRIPTION
remote sources from dependencies specified. Additional manifests beyond
the default one can be specified with the -s option.
- The cargo vendor command will also print out the configuration necessary
- to use the vendored sources, which you will need to add to
- .cargo/config.toml.
+ The configuration necessary to use the vendored sources would be printed
+ to stdout after cargo vendor completes the vendoring process. You will
+ need to add or redirect it to your Cargo configuration file, which is
+ usually .cargo/config.toml locally for the current package.
OPTIONS
Vendor Options
@@ -157,6 +158,10 @@ EXAMPLES
cargo vendor -s ../path/to/Cargo.toml
+ 4. Vendor and redirect the necessary vendor configs to a config file.
+
+ cargo vendor > path/to/my/cargo/config.toml
+
SEE ALSO
cargo(1)
diff --git a/src/tools/cargo/src/doc/man/includes/options-new.md b/src/tools/cargo/src/doc/man/includes/options-new.md
index e9792f05e..0b39ae34f 100644
--- a/src/tools/cargo/src/doc/man/includes/options-new.md
+++ b/src/tools/cargo/src/doc/man/includes/options-new.md
@@ -11,7 +11,7 @@ Create a package with a library target (`src/lib.rs`).
{{#option "`--edition` _edition_" }}
Specify the Rust edition to use. Default is 2021.
-Possible values: 2015, 2018, 2021
+Possible values: 2015, 2018, 2021, 2024
{{/option}}
{{#option "`--name` _name_" }}
diff --git a/src/tools/cargo/src/doc/man/includes/options-profile-legacy-check.md b/src/tools/cargo/src/doc/man/includes/options-profile-legacy-check.md
index 0ec82e693..cb38df0f9 100644
--- a/src/tools/cargo/src/doc/man/includes/options-profile-legacy-check.md
+++ b/src/tools/cargo/src/doc/man/includes/options-profile-legacy-check.md
@@ -6,5 +6,5 @@ test mode which will enable checking tests and enable the `test` cfg option.
See [rustc tests](https://doc.rust-lang.org/rustc/tests/index.html) for more
detail.
-See the [the reference](../reference/profiles.html) for more details on profiles.
+See [the reference](../reference/profiles.html) for more details on profiles.
{{/option}}
diff --git a/src/tools/cargo/src/doc/man/includes/options-profile.md b/src/tools/cargo/src/doc/man/includes/options-profile.md
index 2452e7b14..242ca2254 100644
--- a/src/tools/cargo/src/doc/man/includes/options-profile.md
+++ b/src/tools/cargo/src/doc/man/includes/options-profile.md
@@ -1,4 +1,4 @@
{{#option "`--profile` _name_" }}
{{actionverb}} with the given profile.
-See the [the reference](../reference/profiles.html) for more details on profiles.
+See [the reference](../reference/profiles.html) for more details on profiles.
{{/option}}
diff --git a/src/tools/cargo/src/doc/src/commands/cargo-add.md b/src/tools/cargo/src/doc/src/commands/cargo-add.md
index 03485e121..175111495 100644
--- a/src/tools/cargo/src/doc/src/commands/cargo-add.md
+++ b/src/tools/cargo/src/doc/src/commands/cargo-add.md
@@ -32,7 +32,7 @@ When you add a package that is already present, the existing entry will be updat
Upon successful invocation, the enabled (`+`) and disabled (`-`) [features] of the specified
dependency will be listed in the command's output.
-[features]: ../reference/features.md
+[features]: ../reference/features.html
## OPTIONS
diff --git a/src/tools/cargo/src/doc/src/commands/cargo-bench.md b/src/tools/cargo/src/doc/src/commands/cargo-bench.md
index 45584023e..d50b35f11 100644
--- a/src/tools/cargo/src/doc/src/commands/cargo-bench.md
+++ b/src/tools/cargo/src/doc/src/commands/cargo-bench.md
@@ -30,7 +30,7 @@ Benchmarks are built with the `--test` option to `rustc` which creates a
special executable by linking your code with libtest. The executable
automatically runs all functions annotated with the `#[bench]` attribute.
Cargo passes the `--bench` flag to the test harness to tell it to run
-only benchmarks.
+only benchmarks, regardless of whether the harness is libtest or a custom harness.
The libtest harness may be disabled by setting `harness = false` in the target
manifest settings, in which case your code will need to provide its own `main`
@@ -278,7 +278,7 @@ target artifacts are placed in a separate directory. See the
<dt class="option-term" id="option-cargo-bench---profile"><a class="option-anchor" href="#option-cargo-bench---profile"></a><code>--profile</code> <em>name</em></dt>
<dd class="option-desc">Benchmark with the given profile.
-See the <a href="../reference/profiles.html">the reference</a> for more details on profiles.</dd>
+See <a href="../reference/profiles.html">the reference</a> for more details on profiles.</dd>
diff --git a/src/tools/cargo/src/doc/src/commands/cargo-build.md b/src/tools/cargo/src/doc/src/commands/cargo-build.md
index 8e517bd1f..70c38a05b 100644
--- a/src/tools/cargo/src/doc/src/commands/cargo-build.md
+++ b/src/tools/cargo/src/doc/src/commands/cargo-build.md
@@ -199,7 +199,7 @@ See also the <code>--profile</code> option for choosing a specific profile by na
<dt class="option-term" id="option-cargo-build---profile"><a class="option-anchor" href="#option-cargo-build---profile"></a><code>--profile</code> <em>name</em></dt>
<dd class="option-desc">Build with the given profile.
-See the <a href="../reference/profiles.html">the reference</a> for more details on profiles.</dd>
+See <a href="../reference/profiles.html">the reference</a> for more details on profiles.</dd>
diff --git a/src/tools/cargo/src/doc/src/commands/cargo-check.md b/src/tools/cargo/src/doc/src/commands/cargo-check.md
index 3d7d0a490..1bb0f85c1 100644
--- a/src/tools/cargo/src/doc/src/commands/cargo-check.md
+++ b/src/tools/cargo/src/doc/src/commands/cargo-check.md
@@ -198,7 +198,7 @@ See also the <code>--profile</code> option for choosing a specific profile by na
test mode which will enable checking tests and enable the <code>test</code> cfg option.
See <a href="https://doc.rust-lang.org/rustc/tests/index.html">rustc tests</a> for more
detail.</p>
-<p>See the <a href="../reference/profiles.html">the reference</a> for more details on profiles.</dd>
+<p>See <a href="../reference/profiles.html">the reference</a> for more details on profiles.</dd>
diff --git a/src/tools/cargo/src/doc/src/commands/cargo-doc.md b/src/tools/cargo/src/doc/src/commands/cargo-doc.md
index 92843838c..aebb04c9d 100644
--- a/src/tools/cargo/src/doc/src/commands/cargo-doc.md
+++ b/src/tools/cargo/src/doc/src/commands/cargo-doc.md
@@ -172,7 +172,7 @@ See also the <code>--profile</code> option for choosing a specific profile by na
<dt class="option-term" id="option-cargo-doc---profile"><a class="option-anchor" href="#option-cargo-doc---profile"></a><code>--profile</code> <em>name</em></dt>
<dd class="option-desc">Document with the given profile.
-See the <a href="../reference/profiles.html">the reference</a> for more details on profiles.</dd>
+See <a href="../reference/profiles.html">the reference</a> for more details on profiles.</dd>
diff --git a/src/tools/cargo/src/doc/src/commands/cargo-fix.md b/src/tools/cargo/src/doc/src/commands/cargo-fix.md
index 4dde83d96..9211cf7f8 100644
--- a/src/tools/cargo/src/doc/src/commands/cargo-fix.md
+++ b/src/tools/cargo/src/doc/src/commands/cargo-fix.md
@@ -278,7 +278,7 @@ See also the <code>--profile</code> option for choosing a specific profile by na
test mode which will enable checking tests and enable the <code>test</code> cfg option.
See <a href="https://doc.rust-lang.org/rustc/tests/index.html">rustc tests</a> for more
detail.</p>
-<p>See the <a href="../reference/profiles.html">the reference</a> for more details on profiles.</dd>
+<p>See <a href="../reference/profiles.html">the reference</a> for more details on profiles.</dd>
diff --git a/src/tools/cargo/src/doc/src/commands/cargo-init.md b/src/tools/cargo/src/doc/src/commands/cargo-init.md
index c0cf34b51..70b54802b 100644
--- a/src/tools/cargo/src/doc/src/commands/cargo-init.md
+++ b/src/tools/cargo/src/doc/src/commands/cargo-init.md
@@ -40,7 +40,7 @@ This is the default behavior.</dd>
<dt class="option-term" id="option-cargo-init---edition"><a class="option-anchor" href="#option-cargo-init---edition"></a><code>--edition</code> <em>edition</em></dt>
<dd class="option-desc">Specify the Rust edition to use. Default is 2021.
-Possible values: 2015, 2018, 2021</dd>
+Possible values: 2015, 2018, 2021, 2024</dd>
<dt class="option-term" id="option-cargo-init---name"><a class="option-anchor" href="#option-cargo-init---name"></a><code>--name</code> <em>name</em></dt>
diff --git a/src/tools/cargo/src/doc/src/commands/cargo-install.md b/src/tools/cargo/src/doc/src/commands/cargo-install.md
index 5640af87a..db0ff10db 100644
--- a/src/tools/cargo/src/doc/src/commands/cargo-install.md
+++ b/src/tools/cargo/src/doc/src/commands/cargo-install.md
@@ -94,7 +94,7 @@ will be used, beginning discovery at `$PATH/.cargo/config.toml`.
<dt class="option-term" id="option-cargo-install---vers"><a class="option-anchor" href="#option-cargo-install---vers"></a><code>--vers</code> <em>version</em></dt>
<dt class="option-term" id="option-cargo-install---version"><a class="option-anchor" href="#option-cargo-install---version"></a><code>--version</code> <em>version</em></dt>
-<dd class="option-desc">Specify a version to install. This may be a <a href="../reference/specifying-dependencies.md">version
+<dd class="option-desc">Specify a version to install. This may be a <a href="../reference/specifying-dependencies.html">version
requirement</a>, like <code>~1.2</code>, to have Cargo
select the newest version from the given requirement. If the version does not
have a requirement operator (such as <code>^</code> or <code>~</code>), then it must be in the form
@@ -242,7 +242,7 @@ See also the <code>--profile</code> option for choosing a specific profile by na
<dt class="option-term" id="option-cargo-install---profile"><a class="option-anchor" href="#option-cargo-install---profile"></a><code>--profile</code> <em>name</em></dt>
<dd class="option-desc">Install with the given profile.
-See the <a href="../reference/profiles.html">the reference</a> for more details on profiles.</dd>
+See <a href="../reference/profiles.html">the reference</a> for more details on profiles.</dd>
diff --git a/src/tools/cargo/src/doc/src/commands/cargo-login.md b/src/tools/cargo/src/doc/src/commands/cargo-login.md
index e2efcfe4f..ffdacfc5a 100644
--- a/src/tools/cargo/src/doc/src/commands/cargo-login.md
+++ b/src/tools/cargo/src/doc/src/commands/cargo-login.md
@@ -6,7 +6,7 @@ cargo-login --- Log in to a registry
## SYNOPSIS
-`cargo login` [_options_] [_token_] -- [_args_]
+`cargo login` [_options_] [_token_] [`--` _args_]
## DESCRIPTION
@@ -14,6 +14,8 @@ This command will run a credential provider to save a token so that commands
that require authentication, such as [cargo-publish(1)](cargo-publish.html), will be
automatically authenticated.
+All the arguments following the two dashes (`--`) are passed to the credential provider.
+
For the default `cargo:token` credential provider, the token is saved
in `$CARGO_HOME/credentials.toml`. `CARGO_HOME` defaults to `.cargo`
in your home directory.
diff --git a/src/tools/cargo/src/doc/src/commands/cargo-new.md b/src/tools/cargo/src/doc/src/commands/cargo-new.md
index 144b6f2eb..4e9da6715 100644
--- a/src/tools/cargo/src/doc/src/commands/cargo-new.md
+++ b/src/tools/cargo/src/doc/src/commands/cargo-new.md
@@ -35,7 +35,7 @@ This is the default behavior.</dd>
<dt class="option-term" id="option-cargo-new---edition"><a class="option-anchor" href="#option-cargo-new---edition"></a><code>--edition</code> <em>edition</em></dt>
<dd class="option-desc">Specify the Rust edition to use. Default is 2021.
-Possible values: 2015, 2018, 2021</dd>
+Possible values: 2015, 2018, 2021, 2024</dd>
<dt class="option-term" id="option-cargo-new---name"><a class="option-anchor" href="#option-cargo-new---name"></a><code>--name</code> <em>name</em></dt>
diff --git a/src/tools/cargo/src/doc/src/commands/cargo-run.md b/src/tools/cargo/src/doc/src/commands/cargo-run.md
index c14ad77dc..8c24b8352 100644
--- a/src/tools/cargo/src/doc/src/commands/cargo-run.md
+++ b/src/tools/cargo/src/doc/src/commands/cargo-run.md
@@ -111,7 +111,7 @@ See also the <code>--profile</code> option for choosing a specific profile by na
<dt class="option-term" id="option-cargo-run---profile"><a class="option-anchor" href="#option-cargo-run---profile"></a><code>--profile</code> <em>name</em></dt>
<dd class="option-desc">Run with the given profile.
-See the <a href="../reference/profiles.html">the reference</a> for more details on profiles.</dd>
+See <a href="../reference/profiles.html">the reference</a> for more details on profiles.</dd>
diff --git a/src/tools/cargo/src/doc/src/commands/cargo-rustc.md b/src/tools/cargo/src/doc/src/commands/cargo-rustc.md
index 6ae52d965..f58c8fdda 100644
--- a/src/tools/cargo/src/doc/src/commands/cargo-rustc.md
+++ b/src/tools/cargo/src/doc/src/commands/cargo-rustc.md
@@ -197,7 +197,7 @@ tests</a> for more detail.</li>
<li><code>bench</code> — Builds in the same was as the <a href="cargo-bench.html">cargo-bench(1)</a> command,
similar to the <code>test</code> profile.</li>
</ul>
-<p>See the <a href="../reference/profiles.html">the reference</a> for more details on profiles.</dd>
+<p>See <a href="../reference/profiles.html">the reference</a> for more details on profiles.</dd>
<dt class="option-term" id="option-cargo-rustc---ignore-rust-version"><a class="option-anchor" href="#option-cargo-rustc---ignore-rust-version"></a><code>--ignore-rust-version</code></dt>
diff --git a/src/tools/cargo/src/doc/src/commands/cargo-rustdoc.md b/src/tools/cargo/src/doc/src/commands/cargo-rustdoc.md
index 6635cded5..2e0e67d97 100644
--- a/src/tools/cargo/src/doc/src/commands/cargo-rustdoc.md
+++ b/src/tools/cargo/src/doc/src/commands/cargo-rustdoc.md
@@ -191,7 +191,7 @@ See also the <code>--profile</code> option for choosing a specific profile by na
<dt class="option-term" id="option-cargo-rustdoc---profile"><a class="option-anchor" href="#option-cargo-rustdoc---profile"></a><code>--profile</code> <em>name</em></dt>
<dd class="option-desc">Document with the given profile.
-See the <a href="../reference/profiles.html">the reference</a> for more details on profiles.</dd>
+See <a href="../reference/profiles.html">the reference</a> for more details on profiles.</dd>
diff --git a/src/tools/cargo/src/doc/src/commands/cargo-test.md b/src/tools/cargo/src/doc/src/commands/cargo-test.md
index 6a6ae82d2..ef978570e 100644
--- a/src/tools/cargo/src/doc/src/commands/cargo-test.md
+++ b/src/tools/cargo/src/doc/src/commands/cargo-test.md
@@ -307,7 +307,7 @@ See also the <code>--profile</code> option for choosing a specific profile by na
<dt class="option-term" id="option-cargo-test---profile"><a class="option-anchor" href="#option-cargo-test---profile"></a><code>--profile</code> <em>name</em></dt>
<dd class="option-desc">Test with the given profile.
-See the <a href="../reference/profiles.html">the reference</a> for more details on profiles.</dd>
+See <a href="../reference/profiles.html">the reference</a> for more details on profiles.</dd>
diff --git a/src/tools/cargo/src/doc/src/commands/cargo-vendor.md b/src/tools/cargo/src/doc/src/commands/cargo-vendor.md
index cf47fc256..84560a688 100644
--- a/src/tools/cargo/src/doc/src/commands/cargo-vendor.md
+++ b/src/tools/cargo/src/doc/src/commands/cargo-vendor.md
@@ -16,8 +16,10 @@ the vendor directory specified by `<path>` will contain all remote sources from
dependencies specified. Additional manifests beyond the default one can be
specified with the `-s` option.
-The `cargo vendor` command will also print out the configuration necessary
-to use the vendored sources, which you will need to add to `.cargo/config.toml`.
+The configuration necessary to use the vendored sources would be printed to
+stdout after `cargo vendor` completes the vendoring process.
+You will need to add or redirect it to your Cargo configuration file,
+which is usually `.cargo/config.toml` locally for the current package.
## OPTIONS
@@ -189,6 +191,10 @@ details on environment variables that Cargo reads.
cargo vendor -s ../path/to/Cargo.toml
+4. Vendor and redirect the necessary vendor configs to a config file.
+
+ cargo vendor > path/to/my/cargo/config.toml
+
## SEE ALSO
[cargo(1)](cargo.html)
diff --git a/src/tools/cargo/src/doc/src/reference/config.md b/src/tools/cargo/src/doc/src/reference/config.md
index 6a479b81b..25e17ac90 100644
--- a/src/tools/cargo/src/doc/src/reference/config.md
+++ b/src/tools/cargo/src/doc/src/reference/config.md
@@ -126,6 +126,7 @@ inherits = "dev" # Inherits settings from [profile.dev].
opt-level = 0 # Optimization level.
debug = true # Include debug info.
split-debuginfo = '...' # Debug info splitting behavior.
+strip = "none" # Removes symbols or debuginfo.
debug-assertions = true # Enables debug assertions.
overflow-checks = true # Enables runtime integer overflow checks.
lto = false # Sets link-time optimization.
@@ -133,7 +134,6 @@ panic = 'unwind' # The panic strategy.
incremental = true # Incremental compilation.
codegen-units = 16 # Number of code generation units.
rpath = false # Sets the rpath linking option.
-strip = "none" # Removes symbols or debuginfo.
[profile.<name>.build-override] # Overrides build-script settings.
# Same keys for a normal profile.
[profile.<name>.package.<name>] # Override profile for a package.
@@ -180,6 +180,7 @@ metadata_key2 = "value"
quiet = false # whether cargo output is quiet
verbose = false # whether cargo provides verbose output
color = 'auto' # whether cargo colorizes output
+hyperlinks = true # whether cargo inserts links into output
progress.when = 'auto' # whether cargo shows progress bar
progress.width = 80 # width of progress bar
```
@@ -889,6 +890,13 @@ See [debug](profiles.md#debug).
See [split-debuginfo](profiles.md#split-debuginfo).
+#### `profile.<name>.strip`
+* Type: string or boolean
+* Default: See profile docs.
+* Environment: `CARGO_PROFILE_<name>_STRIP`
+
+See [strip](profiles.md#strip).
+
#### `profile.<name>.debug-assertions`
* Type: boolean
* Default: See profile docs.
@@ -926,21 +934,21 @@ See [opt-level](profiles.md#opt-level).
#### `profile.<name>.panic`
* Type: string
-* default: See profile docs.
+* Default: See profile docs.
* Environment: `CARGO_PROFILE_<name>_PANIC`
See [panic](profiles.md#panic).
#### `profile.<name>.rpath`
* Type: boolean
-* default: See profile docs.
+* Default: See profile docs.
* Environment: `CARGO_PROFILE_<name>_RPATH`
See [rpath](profiles.md#rpath).
#### `profile.<name>.strip`
* Type: string
-* default: See profile docs.
+* Default: See profile docs.
* Environment: `CARGO_PROFILE_<name>_STRIP`
See [strip](profiles.md#strip).
@@ -1264,6 +1272,13 @@ Controls whether or not colored output is used in the terminal. Possible values:
Can be overridden with the `--color` command-line option.
+#### `term.hyperlinks`
+* Type: bool
+* Default: auto-detect
+* Environment: `CARGO_TERM_HYPERLINKS`
+
+Controls whether or not hyperlinks are used in the terminal.
+
#### `term.progress.when`
* Type: string
* Default: "auto"
diff --git a/src/tools/cargo/src/doc/src/reference/environment-variables.md b/src/tools/cargo/src/doc/src/reference/environment-variables.md
index 37c788f8d..3f0552613 100644
--- a/src/tools/cargo/src/doc/src/reference/environment-variables.md
+++ b/src/tools/cargo/src/doc/src/reference/environment-variables.md
@@ -396,7 +396,7 @@ let out_dir = env::var("OUT_DIR").unwrap();
* `CARGO_PKG_<var>` --- The package information variables, with the same names and values as are [provided during crate building][variables set for crates].
[`tracing`]: https://docs.rs/tracing
-[debug logging]: https://doc.crates.io/contrib/architecture/console.html#debug-logging
+[debug logging]: https://doc.crates.io/contrib/implementation/debugging.html#logging
[unix-like platforms]: ../../reference/conditional-compilation.html#unix-and-windows
[windows-like platforms]: ../../reference/conditional-compilation.html#unix-and-windows
[target family]: ../../reference/conditional-compilation.html#target_family
diff --git a/src/tools/cargo/src/doc/src/reference/features.md b/src/tools/cargo/src/doc/src/reference/features.md
index 9e521049c..e3a845d95 100644
--- a/src/tools/cargo/src/doc/src/reference/features.md
+++ b/src/tools/cargo/src/doc/src/reference/features.md
@@ -7,6 +7,13 @@ either be enabled or disabled. Features for the package being built can be
enabled on the command-line with flags such as `--features`. Features for
dependencies can be enabled in the dependency declaration in `Cargo.toml`.
+> **Note**: New crates or versions published on crates.io are now limited to
+> a maximum of 300 features. Exceptions are granted on a case-by-case basis.
+> See this [blog post] for details. Participation in solution discussions is
+> encouraged via the crates.io Zulip stream.
+
+[blog post]: https://blog.rust-lang.org/2023/10/26/broken-badges-and-23k-keywords.html
+
See also the [Features Examples] chapter for some examples of how features can
be used.
diff --git a/src/tools/cargo/src/doc/src/reference/manifest.md b/src/tools/cargo/src/doc/src/reference/manifest.md
index 5ecbe5117..e3168a47f 100644
--- a/src/tools/cargo/src/doc/src/reference/manifest.md
+++ b/src/tools/cargo/src/doc/src/reference/manifest.md
@@ -109,6 +109,8 @@ resolve dependencies, and for guidelines on setting your own version. See the
[SemVer compatibility] chapter for more details on exactly what constitutes a
breaking change.
+This field is optional and defaults to `0.0.0`. The field is required for publishing packages.
+
[Resolver]: resolver.md
[SemVer compatibility]: semver.md
@@ -258,9 +260,9 @@ The `license` field contains the name of the software license that the package
is released under. The `license-file` field contains the path to a file
containing the text of the license (relative to this `Cargo.toml`).
-[crates.io] interprets the `license` field as an [SPDX 2.1 license
-expression][spdx-2.1-license-expressions]. The name must be a known license
-from the [SPDX license list 3.11][spdx-license-list-3.11]. Parentheses are not
+[crates.io] interprets the `license` field as an [SPDX 2.3 license
+expression][spdx-2.3-license-expressions]. The name must be a known license
+from the [SPDX license list 3.20][spdx-license-list-3.20]. Parentheses are not
currently supported. See the [SPDX site] for more information.
SPDX license expressions support AND and OR operators to combine multiple
@@ -470,23 +472,22 @@ if any of those files change.
### The `publish` field
-The `publish` field can be used to prevent a package from being published to a
-package registry (like *crates.io*) by mistake, for instance to keep a package
-private in a company.
-
+The `publish` field can be used to control which registries names the package
+may be published to:
```toml
[package]
# ...
-publish = false
+publish = ["some-registry-name"]
```
-The value may also be an array of strings which are registry names that are
-allowed to be published to.
-
+To prevent a package from being published to a registry (like crates.io) by mistake,
+for instance to keep a package private in a company,
+you can omit the [`version`](#the-version-field) field.
+If you'd like to be more explicit, you can disable publishing:
```toml
[package]
# ...
-publish = ["some-registry-name"]
+publish = false
```
If publish array contains a single registry, `cargo publish` command will use
@@ -511,6 +512,10 @@ package-name = "my-awesome-android-app"
assets = "path/to/static"
```
+You'll need to look in the documentation for your tool to see how to use this field.
+For Rust Projects that use `package.metadata` tables, see:
+- [docs.rs](https://docs.rs/about/metadata)
+
There is a similar table at the workspace level at
[`workspace.metadata`][workspace-metadata]. While cargo does not specify a
format for the content of either of these tables, it is suggested that
@@ -628,9 +633,9 @@ more detail.
[docs.rs]: https://docs.rs/
[publishing]: publishing.md
[Rust Edition]: ../../edition-guide/index.html
-[spdx-2.1-license-expressions]: https://spdx.org/spdx-specification-21-web-version#h.jxpfx0ykyb60
-[spdx-license-list-3.11]: https://github.com/spdx/license-list-data/tree/v3.11
-[SPDX site]: https://spdx.org/license-list
+[spdx-2.3-license-expressions]: https://spdx.github.io/spdx-spec/v2.3/SPDX-license-expressions/
+[spdx-license-list-3.20]: https://github.com/spdx/license-list-data/tree/v3.20
+[SPDX site]: https://spdx.org
[TOML]: https://toml.io/
<script>
diff --git a/src/tools/cargo/src/doc/src/reference/profiles.md b/src/tools/cargo/src/doc/src/reference/profiles.md
index 15ca8953c..165b41d60 100644
--- a/src/tools/cargo/src/doc/src/reference/profiles.md
+++ b/src/tools/cargo/src/doc/src/reference/profiles.md
@@ -270,7 +270,7 @@ The default settings for the `dev` profile are:
opt-level = 0
debug = true
split-debuginfo = '...' # Platform-specific.
-strip = false
+strip = "none"
debug-assertions = true
overflow-checks = true
lto = false
@@ -293,7 +293,7 @@ The default settings for the `release` profile are:
opt-level = 3
debug = false
split-debuginfo = '...' # Platform-specific.
-strip = false
+strip = "none"
debug-assertions = false
overflow-checks = false
lto = false
diff --git a/src/tools/cargo/src/doc/src/reference/publishing.md b/src/tools/cargo/src/doc/src/reference/publishing.md
index 54a8635fb..5dcb73d0f 100644
--- a/src/tools/cargo/src/doc/src/reference/publishing.md
+++ b/src/tools/cargo/src/doc/src/reference/publishing.md
@@ -139,7 +139,7 @@ Then run [`cargo publish`] as described above to upload the new version.
> **Recommendation:** Consider the full release process and automate what you can.
>
> Each version should include:
-> - A changelog entry, preferrably [manually curated](https://keepachangelog.com/en/1.0.0/) though a generated one is better than nothing
+> - A changelog entry, preferably [manually curated](https://keepachangelog.com/en/1.0.0/) though a generated one is better than nothing
> - A [git tag](https://git-scm.com/book/en/v2/Git-Basics-Tagging) pointing to the published commit
>
> Examples of third-party tools that are representative of different workflows include (in alphabetical order):
diff --git a/src/tools/cargo/src/doc/src/reference/registry-authentication.md b/src/tools/cargo/src/doc/src/reference/registry-authentication.md
index 9f8f7e979..f07bf7066 100644
--- a/src/tools/cargo/src/doc/src/reference/registry-authentication.md
+++ b/src/tools/cargo/src/doc/src/reference/registry-authentication.md
@@ -103,7 +103,7 @@ Install the provider with `cargo install cargo-credential-1password`
In the config, add to (or create) `registry.global-credential-providers`:
```toml
[registry]
-global-credential-providers = ["cargo:token", "cargo-credential-1password --email you@example.com"]
+global-credential-providers = ["cargo:token", "cargo-credential-1password --account my.1password.com"]
```
The values in `global-credential-providers` are split on spaces into path and command-line arguments. To
diff --git a/src/tools/cargo/src/doc/src/reference/resolver.md b/src/tools/cargo/src/doc/src/reference/resolver.md
index 7d01fb167..de9d21fbd 100644
--- a/src/tools/cargo/src/doc/src/reference/resolver.md
+++ b/src/tools/cargo/src/doc/src/reference/resolver.md
@@ -489,9 +489,43 @@ break the build.
The following illustrates some problems you may experience, and some possible
solutions.
+### Why was a dependency included?
+
+Say you see dependency `rand` in the `cargo check` output but don't think its needed and want to understand why its being pulled in.
+
+You can run
+```console
+$ cargo tree --workspace --target all --all-features --invert rand
+rand v0.8.5
+└── ...
+
+rand v0.8.5
+└── ...
+```
+
+You might identify that it was an activated feature that caused `rand` to show up. To figure out which package activated the feature, you can add the `--edges features`
+```console
+$ cargo tree --workspace --target all --all-features --edges features --invert rand
+rand v0.8.5
+└── ...
+
+rand v0.8.5
+└── ...
+```
+
### Unexpected dependency duplication
-The resolver algorithm may converge on a solution that includes two copies of a
+You see multiple instances of `rand` when you run
+```console
+$ cargo tree --workspace --target all --all-features --duplicates
+rand v0.7.3
+└── ...
+
+rand v0.8.5
+└── ...
+```
+
+The resolver algorithm has converged on a solution that includes two copies of a
dependency when one would suffice. For example:
```toml
@@ -517,6 +551,17 @@ But, if you run into this situation, the [`cargo update`] command with the
[`cargo update`]: ../commands/cargo-update.md
+### Why wasn't a newer version selected?
+
+Say you noticed that the latest version of a dependency wasn't selected when you ran:
+```console
+$ cargo update
+```
+You can enable some extra logging to see why this happened:
+```console
+$ env CARGO_LOG=cargo::core::resolver=trace cargo update
+```
+**Note:** Cargo log targets and levels may change over time.
### SemVer-breaking patch release breaks the build
diff --git a/src/tools/cargo/src/doc/src/reference/specifying-dependencies.md b/src/tools/cargo/src/doc/src/reference/specifying-dependencies.md
index 746b5fcb2..2bdbbceee 100644
--- a/src/tools/cargo/src/doc/src/reference/specifying-dependencies.md
+++ b/src/tools/cargo/src/doc/src/reference/specifying-dependencies.md
@@ -135,7 +135,7 @@ separated with a comma, e.g., `>= 1.2, < 1.5`.
> Avoid constraining the upper bound of a version to be anything less than the
> next semver incompatible version
> (e.g. avoid `">=2.0, <2.4"`) as other packages in the dependency tree may
-> require a newer version, leading to an unresolvable error (see #6584).
+> require a newer version, leading to an unresolvable error (see [#9029]).
> Consider whether controlling the version in your [`Cargo.lock`] would be more
> appropriate.
>
@@ -152,7 +152,7 @@ separated with a comma, e.g., `>= 1.2, < 1.5`.
[`Cargo.lock`]: ../guide/cargo-toml-vs-cargo-lock.md
[#2222]: https://github.com/rust-lang/cargo/issues/2222
-[#6584]: https://github.com/rust-lang/cargo/issues/6584
+[#9029]: https://github.com/rust-lang/cargo/issues/9029
[#10599]: https://github.com/rust-lang/cargo/issues/10599
## Specifying dependencies from other registries
diff --git a/src/tools/cargo/src/doc/src/reference/unstable.md b/src/tools/cargo/src/doc/src/reference/unstable.md
index c8047de90..0683daa3c 100644
--- a/src/tools/cargo/src/doc/src/reference/unstable.md
+++ b/src/tools/cargo/src/doc/src/reference/unstable.md
@@ -93,6 +93,8 @@ For the latest nightly, see the [nightly version] of this page.
* [codegen-backend](#codegen-backend) --- Select the codegen backend used by rustc.
* [per-package-target](#per-package-target) --- Sets the `--target` to use for each individual package.
* [artifact dependencies](#artifact-dependencies) --- Allow build artifacts to be included into other build artifacts and build them for different targets.
+ * [Edition 2024](#edition-2024) — Adds support for the 2024 Edition.
+ * [Profile `trim-paths` option](#profile-trim-paths-option) --- Control the sanitization of file paths in build outputs.
* Information and metadata
* [Build-plan](#build-plan) --- Emits JSON information on which commands will be run.
* [unit-graph](#unit-graph) --- Emits JSON for Cargo's internal graph structure.
@@ -1079,34 +1081,14 @@ you are ok with dev-deps being build for `cargo doc`.
* RFC: [#3013](https://github.com/rust-lang/rfcs/pull/3013)
* Tracking Issue: [#10554](https://github.com/rust-lang/cargo/issues/10554)
-`-Z check-cfg` command line enables compile time checking of name and values in `#[cfg]`, `cfg!`,
-`#[link]` and `#[cfg_attr]` with the `rustc` and `rustdoc` unstable `--check-cfg` command line.
+`-Z check-cfg` command line enables compile time checking of Cargo features as well as `rustc`
+well known names and values in `#[cfg]`, `cfg!`, `#[link]` and `#[cfg_attr]` with the `rustc`
+and `rustdoc` unstable `--check-cfg` command line.
-It's values are:
- - `features`: enables features checking via `--check-cfg=values(feature, ...)`.
- Note than this command line options will probably become the default when stabilizing.
- - `names`: enables well known names checking via `--check-cfg=names()`.
- - `values`: enables well known values checking via `--check-cfg=values()`.
- - `output`: enable the use of `rustc-check-cfg` in build script.
+You can use the flag like this:
-For instance:
-
-```
-cargo check -Z unstable-options -Z check-cfg=features
-cargo check -Z unstable-options -Z check-cfg=names
-cargo check -Z unstable-options -Z check-cfg=values
-cargo check -Z unstable-options -Z check-cfg=features,names,values
```
-
-Or for `output`:
-
-```rust,no_run
-// build.rs
-println!("cargo:rustc-check-cfg=names(foo, bar)");
-```
-
-```
-cargo check -Z unstable-options -Z check-cfg=output
+cargo check -Z unstable-options -Z check-cfg
```
### `cargo:rustc-check-cfg=CHECK_CFG`
@@ -1115,12 +1097,23 @@ The `rustc-check-cfg` instruction tells Cargo to pass the given value to the
`--check-cfg` flag to the compiler. This may be used for compile-time
detection of unexpected conditional compilation name and/or values.
-This can only be used in combination with `-Zcheck-cfg=output` otherwise it is ignored
+This can only be used in combination with `-Zcheck-cfg` otherwise it is ignored
with a warning.
-If you want to integrate with Cargo features, use `-Zcheck-cfg=features` instead of
+If you want to integrate with Cargo features, only use `-Zcheck-cfg` instead of
trying to do it manually with this option.
+You can use the instruction like this:
+
+```rust,no_run
+// build.rs
+println!("cargo:rustc-check-cfg=cfg(foo, bar)");
+```
+
+```
+cargo check -Z unstable-options -Z check-cfg
+```
+
## codegen-backend
The `codegen-backend` feature makes it possible to select the codegen backend used by rustc using a profile.
@@ -1229,9 +1222,6 @@ at the start of the infostring at the top of the file.
Inferred / defaulted manifest fields:
- `package.name = <slugified file stem>`
-- `package.version = "0.0.0"` to [call attention to this crate being used in unexpected places](https://matklad.github.io/2021/08/22/large-rust-workspaces.html#Smaller-Tips)
-- `package.publish = false` to avoid accidental publishes, particularly if we
- later add support for including them in a workspace.
- `package.edition = <current>` to avoid always having to add an embedded
manifest at the cost of potentially breaking scripts on rust upgrades
- Warn when `edition` is unspecified to raise awareness of this
@@ -1271,6 +1261,128 @@ Differences between `cargo run --manifest-path <path>` and `cargo <path>`
### Documentation Updates
+## Edition 2024
+* Tracking Issue: (none created yet)
+* RFC: [rust-lang/rfcs#3501](https://github.com/rust-lang/rfcs/pull/3501)
+
+Support for the 2024 [edition] can be enabled by adding the `edition2024`
+unstable feature to the top of `Cargo.toml`:
+
+```toml
+cargo-features = ["edition2024"]
+
+[package]
+name = "my-package"
+version = "0.1.0"
+edition = "2024"
+```
+
+If you want to transition an existing project from a previous edition, then
+`cargo fix --edition` can be used on the nightly channel. After running `cargo
+fix`, you can switch the edition to 2024 as illustrated above.
+
+This feature is very unstable, and is only intended for early testing and
+experimentation. Future nightly releases may introduce changes for the 2024
+edition that may break your build.
+
+[edition]: ../../edition-guide/index.html
+
+## Profile `trim-paths` option
+
+* Tracking Issue: [rust-lang/cargo#12137](https://github.com/rust-lang/cargo/issues/12137)
+* Tracking Rustc Issue: [rust-lang/rust#111540](https://github.com/rust-lang/rust/issues/111540)
+
+This adds a new profile setting to control how paths are sanitized in the resulting binary.
+This can be enabled like so:
+
+```toml
+cargo-features = ["trim-paths"]
+
+[package]
+# ...
+
+[profile.release]
+trim-paths = ["diagnostics", "object"]
+```
+
+To set this in a profile in Cargo configuration,
+you need to use either `-Z trim-paths` or `[unstable]` table to enable it.
+For example,
+
+```toml
+# .cargo/config.toml
+[unstable]
+trim-paths = true
+
+[profile.release]
+trim-paths = ["diagnostics", "object"]
+```
+
+### Documentation updates
+
+#### trim-paths
+
+*as a new ["Profiles settings" entry](./profiles.html#profile-settings)*
+
+`trim-paths` is a profile setting which enables and controls the sanitization of file paths in build outputs.
+It takes the following values:
+
+- `"none"` and `false` --- disable path sanitization
+- `"macro"` --- sanitize paths in the expansion of `std::file!()` macro.
+ This is where paths in embedded panic messages come from
+- `"diagnostics"` --- sanitize paths in printed compiler diagnostics
+- `"object"` --- sanitize paths in compiled executables or libraries
+- `"all"` and `true` --- sanitize paths in all possible locations
+
+It also takes an array with the combinations of `"macro"`, `"diagnostics"`, and `"object"`.
+
+It is defaulted to `none` for the `dev` profile, and `object` for the `release` profile.
+You can manually override it by specifying this option in `Cargo.toml`:
+
+```toml
+[profile.dev]
+trim-paths = "all"
+
+[profile.release]
+trim-paths = ["object", "diagnostics"]
+```
+
+The default `release` profile setting (`object`) sanitizes only the paths in emitted executable or library files.
+It always affects paths from macros such as panic messages, and in debug information only if they will be embedded together with the binary
+(the default on platforms with ELF binaries, such as Linux and windows-gnu),
+but will not touch them if they are in separate files (the default on Windows MSVC and macOS).
+But the paths to these separate files are sanitized.
+
+If `trim-paths` is not `none` or `false`, then the following paths are sanitized if they appear in a selected scope:
+
+1. Path to the source files of the standard and core library (sysroot) will begin with `/rustc/[rustc commit hash]`,
+ e.g. `/home/username/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/result.rs` ->
+ `/rustc/fe72845f7bb6a77b9e671e6a4f32fe714962cec4/library/core/src/result.rs`
+2. Path to the current package will be stripped, relatively to the current workspace root, e.g. `/home/username/crate/src/lib.rs` -> `src/lib.rs`.
+3. Path to dependency packages will be replaced with `[package name]-[version]`. E.g. `/home/username/deps/foo/src/lib.rs` -> `foo-0.1.0/src/lib.rs`
+
+When a path to the source files of the standard and core library is *not* in scope for sanitization,
+the emitted path will depend on if `rust-src` component is present.
+If it is, then some paths will point to the copy of the source files on your file system;
+if it isn't, then they will show up as `/rustc/[rustc commit hash]/library/...`
+(just like when it is selected for sanitization).
+Paths to all other source files will not be affected.
+
+This will not affect any hard-coded paths in the source code, such as in strings.
+
+#### Environment variable
+
+*as a new entry of ["Environment variables Cargo sets for build scripts"](./environment-variables.md#environment-variables-cargo-sets-for-crates)*
+
+* `CARGO_TRIM_PATHS` --- The value of `trim-paths` profile option.
+ `false`, `"none"`, and empty arrays would be converted to `none`.
+ `true` and `"all"` become `all`.
+ Values in a non-empty array would be joined into a comma-separated list.
+ If the build script introduces absolute paths to built artifacts (such as by invoking a compiler),
+ the user may request them to be sanitized in different types of artifacts.
+ Common paths requiring sanitization include `OUT_DIR` and `CARGO_MANIFEST_DIR`,
+ plus any other introduced by the build script, such as include directories.
+
# Stabilized and removed features
## Compile progress
diff --git a/src/tools/cargo/src/etc/man/cargo-add.1 b/src/tools/cargo/src/etc/man/cargo-add.1
index f69e6d0db..4e8b68566 100644
--- a/src/tools/cargo/src/etc/man/cargo-add.1
+++ b/src/tools/cargo/src/etc/man/cargo-add.1
@@ -44,7 +44,7 @@ If no source is specified, then a best effort will be made to select one, includ
.sp
When you add a package that is already present, the existing entry will be updated with the flags specified.
.sp
-Upon successful invocation, the enabled (\fB+\fR) and disabled (\fB\-\fR) \fIfeatures\fR <https://doc.rust\-lang.org/cargo/reference/features.md> of the specified
+Upon successful invocation, the enabled (\fB+\fR) and disabled (\fB\-\fR) \fIfeatures\fR <https://doc.rust\-lang.org/cargo/reference/features.html> of the specified
dependency will be listed in the command\[cq]s output.
.SH "OPTIONS"
.SS "Source options"
diff --git a/src/tools/cargo/src/etc/man/cargo-bench.1 b/src/tools/cargo/src/etc/man/cargo-bench.1
index 64498c4d6..0f5a993e0 100644
--- a/src/tools/cargo/src/etc/man/cargo-bench.1
+++ b/src/tools/cargo/src/etc/man/cargo-bench.1
@@ -32,7 +32,7 @@ Benchmarks are built with the \fB\-\-test\fR option to \fBrustc\fR which creates
special executable by linking your code with libtest. The executable
automatically runs all functions annotated with the \fB#[bench]\fR attribute.
Cargo passes the \fB\-\-bench\fR flag to the test harness to tell it to run
-only benchmarks.
+only benchmarks, regardless of whether the harness is libtest or a custom harness.
.sp
The libtest harness may be disabled by setting \fBharness = false\fR in the target
manifest settings, in which case your code will need to provide its own \fBmain\fR
@@ -282,7 +282,7 @@ target artifacts are placed in a separate directory. See the
\fB\-\-profile\fR \fIname\fR
.RS 4
Benchmark with the given profile.
-See the \fIthe reference\fR <https://doc.rust\-lang.org/cargo/reference/profiles.html> for more details on profiles.
+See \fIthe reference\fR <https://doc.rust\-lang.org/cargo/reference/profiles.html> for more details on profiles.
.RE
.sp
\fB\-\-ignore\-rust\-version\fR
diff --git a/src/tools/cargo/src/etc/man/cargo-build.1 b/src/tools/cargo/src/etc/man/cargo-build.1
index 6bcfe8093..4194d7b77 100644
--- a/src/tools/cargo/src/etc/man/cargo-build.1
+++ b/src/tools/cargo/src/etc/man/cargo-build.1
@@ -188,7 +188,7 @@ See also the \fB\-\-profile\fR option for choosing a specific profile by name.
\fB\-\-profile\fR \fIname\fR
.RS 4
Build with the given profile.
-See the \fIthe reference\fR <https://doc.rust\-lang.org/cargo/reference/profiles.html> for more details on profiles.
+See \fIthe reference\fR <https://doc.rust\-lang.org/cargo/reference/profiles.html> for more details on profiles.
.RE
.sp
\fB\-\-ignore\-rust\-version\fR
diff --git a/src/tools/cargo/src/etc/man/cargo-check.1 b/src/tools/cargo/src/etc/man/cargo-check.1
index fdbb84647..43d570058 100644
--- a/src/tools/cargo/src/etc/man/cargo-check.1
+++ b/src/tools/cargo/src/etc/man/cargo-check.1
@@ -190,7 +190,7 @@ test mode which will enable checking tests and enable the \fBtest\fR cfg option.
See \fIrustc tests\fR <https://doc.rust\-lang.org/rustc/tests/index.html> for more
detail.
.sp
-See the \fIthe reference\fR <https://doc.rust\-lang.org/cargo/reference/profiles.html> for more details on profiles.
+See \fIthe reference\fR <https://doc.rust\-lang.org/cargo/reference/profiles.html> for more details on profiles.
.RE
.sp
\fB\-\-ignore\-rust\-version\fR
diff --git a/src/tools/cargo/src/etc/man/cargo-doc.1 b/src/tools/cargo/src/etc/man/cargo-doc.1
index 2bdd8867b..69f781de6 100644
--- a/src/tools/cargo/src/etc/man/cargo-doc.1
+++ b/src/tools/cargo/src/etc/man/cargo-doc.1
@@ -157,7 +157,7 @@ See also the \fB\-\-profile\fR option for choosing a specific profile by name.
\fB\-\-profile\fR \fIname\fR
.RS 4
Document with the given profile.
-See the \fIthe reference\fR <https://doc.rust\-lang.org/cargo/reference/profiles.html> for more details on profiles.
+See \fIthe reference\fR <https://doc.rust\-lang.org/cargo/reference/profiles.html> for more details on profiles.
.RE
.sp
\fB\-\-ignore\-rust\-version\fR
diff --git a/src/tools/cargo/src/etc/man/cargo-fix.1 b/src/tools/cargo/src/etc/man/cargo-fix.1
index 61083f214..1f10f179b 100644
--- a/src/tools/cargo/src/etc/man/cargo-fix.1
+++ b/src/tools/cargo/src/etc/man/cargo-fix.1
@@ -285,7 +285,7 @@ test mode which will enable checking tests and enable the \fBtest\fR cfg option.
See \fIrustc tests\fR <https://doc.rust\-lang.org/rustc/tests/index.html> for more
detail.
.sp
-See the \fIthe reference\fR <https://doc.rust\-lang.org/cargo/reference/profiles.html> for more details on profiles.
+See \fIthe reference\fR <https://doc.rust\-lang.org/cargo/reference/profiles.html> for more details on profiles.
.RE
.sp
\fB\-\-ignore\-rust\-version\fR
diff --git a/src/tools/cargo/src/etc/man/cargo-init.1 b/src/tools/cargo/src/etc/man/cargo-init.1
index 56d1aca9f..b37622b52 100644
--- a/src/tools/cargo/src/etc/man/cargo-init.1
+++ b/src/tools/cargo/src/etc/man/cargo-init.1
@@ -37,7 +37,7 @@ Create a package with a library target (\fBsrc/lib.rs\fR).
\fB\-\-edition\fR \fIedition\fR
.RS 4
Specify the Rust edition to use. Default is 2021.
-Possible values: 2015, 2018, 2021
+Possible values: 2015, 2018, 2021, 2024
.RE
.sp
\fB\-\-name\fR \fIname\fR
diff --git a/src/tools/cargo/src/etc/man/cargo-install.1 b/src/tools/cargo/src/etc/man/cargo-install.1
index 5ca3180fd..d4d4d3eb6 100644
--- a/src/tools/cargo/src/etc/man/cargo-install.1
+++ b/src/tools/cargo/src/etc/man/cargo-install.1
@@ -114,7 +114,7 @@ will be used, beginning discovery at \fB$PATH/.cargo/config.toml\fR\&.
\fB\-\-version\fR \fIversion\fR
.RS 4
Specify a version to install. This may be a \fIversion
-requirement\fR <https://doc.rust\-lang.org/cargo/reference/specifying\-dependencies.md>, like \fB~1.2\fR, to have Cargo
+requirement\fR <https://doc.rust\-lang.org/cargo/reference/specifying\-dependencies.html>, like \fB~1.2\fR, to have Cargo
select the newest version from the given requirement. If the version does not
have a requirement operator (such as \fB^\fR or \fB~\fR), then it must be in the form
\fIMAJOR.MINOR.PATCH\fR, and will install exactly that version; it is \fInot\fR
@@ -270,7 +270,7 @@ See also the \fB\-\-profile\fR option for choosing a specific profile by name.
\fB\-\-profile\fR \fIname\fR
.RS 4
Install with the given profile.
-See the \fIthe reference\fR <https://doc.rust\-lang.org/cargo/reference/profiles.html> for more details on profiles.
+See \fIthe reference\fR <https://doc.rust\-lang.org/cargo/reference/profiles.html> for more details on profiles.
.RE
.sp
\fB\-\-ignore\-rust\-version\fR
diff --git a/src/tools/cargo/src/etc/man/cargo-login.1 b/src/tools/cargo/src/etc/man/cargo-login.1
index 92b3b3f3c..16f5773fa 100644
--- a/src/tools/cargo/src/etc/man/cargo-login.1
+++ b/src/tools/cargo/src/etc/man/cargo-login.1
@@ -6,12 +6,14 @@
.SH "NAME"
cargo\-login \[em] Log in to a registry
.SH "SYNOPSIS"
-\fBcargo login\fR [\fIoptions\fR] [\fItoken\fR] \[en] [\fIargs\fR]
+\fBcargo login\fR [\fIoptions\fR] [\fItoken\fR] [\fB\-\-\fR \fIargs\fR]
.SH "DESCRIPTION"
This command will run a credential provider to save a token so that commands
that require authentication, such as \fBcargo\-publish\fR(1), will be
automatically authenticated.
.sp
+All the arguments following the two dashes (\fB\-\-\fR) are passed to the credential provider.
+.sp
For the default \fBcargo:token\fR credential provider, the token is saved
in \fB$CARGO_HOME/credentials.toml\fR\&. \fBCARGO_HOME\fR defaults to \fB\&.cargo\fR
in your home directory.
diff --git a/src/tools/cargo/src/etc/man/cargo-new.1 b/src/tools/cargo/src/etc/man/cargo-new.1
index 62e0eb157..f1939a543 100644
--- a/src/tools/cargo/src/etc/man/cargo-new.1
+++ b/src/tools/cargo/src/etc/man/cargo-new.1
@@ -32,7 +32,7 @@ Create a package with a library target (\fBsrc/lib.rs\fR).
\fB\-\-edition\fR \fIedition\fR
.RS 4
Specify the Rust edition to use. Default is 2021.
-Possible values: 2015, 2018, 2021
+Possible values: 2015, 2018, 2021, 2024
.RE
.sp
\fB\-\-name\fR \fIname\fR
diff --git a/src/tools/cargo/src/etc/man/cargo-run.1 b/src/tools/cargo/src/etc/man/cargo-run.1
index 293814674..2ecbbcf65 100644
--- a/src/tools/cargo/src/etc/man/cargo-run.1
+++ b/src/tools/cargo/src/etc/man/cargo-run.1
@@ -94,7 +94,7 @@ See also the \fB\-\-profile\fR option for choosing a specific profile by name.
\fB\-\-profile\fR \fIname\fR
.RS 4
Run with the given profile.
-See the \fIthe reference\fR <https://doc.rust\-lang.org/cargo/reference/profiles.html> for more details on profiles.
+See \fIthe reference\fR <https://doc.rust\-lang.org/cargo/reference/profiles.html> for more details on profiles.
.RE
.sp
\fB\-\-ignore\-rust\-version\fR
diff --git a/src/tools/cargo/src/etc/man/cargo-rustc.1 b/src/tools/cargo/src/etc/man/cargo-rustc.1
index 501e9208e..f3535b0f0 100644
--- a/src/tools/cargo/src/etc/man/cargo-rustc.1
+++ b/src/tools/cargo/src/etc/man/cargo-rustc.1
@@ -194,7 +194,7 @@ tests\fR <https://doc.rust\-lang.org/rustc/tests/index.html> for more detail.
similar to the \fBtest\fR profile.
.RE
.sp
-See the \fIthe reference\fR <https://doc.rust\-lang.org/cargo/reference/profiles.html> for more details on profiles.
+See \fIthe reference\fR <https://doc.rust\-lang.org/cargo/reference/profiles.html> for more details on profiles.
.RE
.sp
\fB\-\-ignore\-rust\-version\fR
diff --git a/src/tools/cargo/src/etc/man/cargo-rustdoc.1 b/src/tools/cargo/src/etc/man/cargo-rustdoc.1
index 0335d6e54..72a182de8 100644
--- a/src/tools/cargo/src/etc/man/cargo-rustdoc.1
+++ b/src/tools/cargo/src/etc/man/cargo-rustdoc.1
@@ -176,7 +176,7 @@ See also the \fB\-\-profile\fR option for choosing a specific profile by name.
\fB\-\-profile\fR \fIname\fR
.RS 4
Document with the given profile.
-See the \fIthe reference\fR <https://doc.rust\-lang.org/cargo/reference/profiles.html> for more details on profiles.
+See \fIthe reference\fR <https://doc.rust\-lang.org/cargo/reference/profiles.html> for more details on profiles.
.RE
.sp
\fB\-\-ignore\-rust\-version\fR
diff --git a/src/tools/cargo/src/etc/man/cargo-test.1 b/src/tools/cargo/src/etc/man/cargo-test.1
index 8e460e167..467000147 100644
--- a/src/tools/cargo/src/etc/man/cargo-test.1
+++ b/src/tools/cargo/src/etc/man/cargo-test.1
@@ -309,7 +309,7 @@ See also the \fB\-\-profile\fR option for choosing a specific profile by name.
\fB\-\-profile\fR \fIname\fR
.RS 4
Test with the given profile.
-See the \fIthe reference\fR <https://doc.rust\-lang.org/cargo/reference/profiles.html> for more details on profiles.
+See \fIthe reference\fR <https://doc.rust\-lang.org/cargo/reference/profiles.html> for more details on profiles.
.RE
.sp
\fB\-\-ignore\-rust\-version\fR
diff --git a/src/tools/cargo/src/etc/man/cargo-vendor.1 b/src/tools/cargo/src/etc/man/cargo-vendor.1
index cb46f67cd..67744532e 100644
--- a/src/tools/cargo/src/etc/man/cargo-vendor.1
+++ b/src/tools/cargo/src/etc/man/cargo-vendor.1
@@ -14,8 +14,10 @@ the vendor directory specified by \fB<path>\fR will contain all remote sources f
dependencies specified. Additional manifests beyond the default one can be
specified with the \fB\-s\fR option.
.sp
-The \fBcargo vendor\fR command will also print out the configuration necessary
-to use the vendored sources, which you will need to add to \fB\&.cargo/config.toml\fR\&.
+The configuration necessary to use the vendored sources would be printed to
+stdout after \fBcargo vendor\fR completes the vendoring process.
+You will need to add or redirect it to your Cargo configuration file,
+which is usually \fB\&.cargo/config.toml\fR locally for the current package.
.SH "OPTIONS"
.SS "Vendor Options"
.sp
@@ -205,5 +207,15 @@ cargo vendor \-s ../path/to/Cargo.toml
.fi
.RE
.RE
+.sp
+.RS 4
+\h'-04' 4.\h'+01'Vendor and redirect the necessary vendor configs to a config file.
+.sp
+.RS 4
+.nf
+cargo vendor > path/to/my/cargo/config.toml
+.fi
+.RE
+.RE
.SH "SEE ALSO"
\fBcargo\fR(1)
diff --git a/src/tools/cargo/tests/testsuite/artifact_dep.rs b/src/tools/cargo/tests/testsuite/artifact_dep.rs
index 64aa9d8af..c51298735 100644
--- a/src/tools/cargo/tests/testsuite/artifact_dep.rs
+++ b/src/tools/cargo/tests/testsuite/artifact_dep.rs
@@ -2152,6 +2152,7 @@ fn doc_lib_true() {
[DOCUMENTING] bar v0.0.1 ([CWD]/bar)
[DOCUMENTING] foo v0.0.1 ([CWD])
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+[GENERATED] [CWD]/target/doc/foo/index.html
",
)
.run();
@@ -2227,6 +2228,7 @@ fn rustdoc_works_on_libs_with_artifacts_and_lib_false() {
[COMPILING] bar v0.5.0 ([CWD]/bar)
[DOCUMENTING] foo v0.0.1 ([CWD])
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+[GENERATED] [CWD]/target/doc/foo/index.html
",
)
.run();
diff --git a/src/tools/cargo/tests/testsuite/bad_config.rs b/src/tools/cargo/tests/testsuite/bad_config.rs
index 82da880ea..1d3bb9ee1 100644
--- a/src/tools/cargo/tests/testsuite/bad_config.rs
+++ b/src/tools/cargo/tests/testsuite/bad_config.rs
@@ -373,7 +373,7 @@ Caused by:
failed to clone into: [..]
Caused by:
- URLs need to specify the path to the repository
+ URL \"git://host.xz\" does not specify a path to a repository
"
} else {
"\
@@ -1664,3 +1664,37 @@ note: Sources are not allowed to be defined multiple times.
)
.run();
}
+
+#[cargo_test]
+fn bad_trim_paths() {
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.0"
+
+ [profile.dev]
+ trim-paths = "split-debuginfo"
+ "#,
+ )
+ .file("src/lib.rs", "")
+ .build();
+
+ p.cargo("check -Ztrim-paths")
+ .masquerade_as_nightly_cargo(&["trim-paths"])
+ .with_status(101)
+ .with_stderr(
+ r#"error: failed to parse manifest at `[..]`
+
+Caused by:
+ TOML parse error at line 7, column 30
+ |
+ 7 | trim-paths = "split-debuginfo"
+ | ^^^^^^^^^^^^^^^^^
+ expected a boolean, "none", "diagnostics", "macro", "object", "all", or an array with these options
+"#,
+ )
+ .run();
+}
diff --git a/src/tools/cargo/tests/testsuite/build.rs b/src/tools/cargo/tests/testsuite/build.rs
index 1afa83918..23840ad9a 100644
--- a/src/tools/cargo/tests/testsuite/build.rs
+++ b/src/tools/cargo/tests/testsuite/build.rs
@@ -159,6 +159,29 @@ For more information, try '--help'.
}
#[cargo_test]
+fn cargo_compile_with_unsupported_short_config_flag() {
+ let p = project()
+ .file("Cargo.toml", &basic_bin_manifest("foo"))
+ .file("src/foo.rs", &main_file(r#""i am foo""#, &[]))
+ .build();
+
+ p.cargo("build -c net.git-fetch-with-cli=true")
+ .with_stderr(
+ "\
+error: unexpected argument '-c' found
+
+ tip: a similar argument exists: '--config'
+
+Usage: cargo[EXE] build [OPTIONS]
+
+For more information, try '--help'.
+",
+ )
+ .with_status(1)
+ .run();
+}
+
+#[cargo_test]
fn cargo_compile_with_workspace_excluded() {
let p = project().file("src/main.rs", "fn main() {}").build();
@@ -183,6 +206,30 @@ fn cargo_compile_manifest_path() {
}
#[cargo_test]
+fn cargo_compile_with_wrong_manifest_path_flag() {
+ let p = project()
+ .file("Cargo.toml", &basic_bin_manifest("foo"))
+ .file("src/foo.rs", &main_file(r#""i am foo""#, &[]))
+ .build();
+
+ p.cargo("build --path foo/Cargo.toml")
+ .cwd(p.root().parent().unwrap())
+ .with_stderr(
+ "\
+error: unexpected argument '--path' found
+
+ tip: a similar argument exists: '--manifest-path'
+
+Usage: cargo[EXE] build [OPTIONS]
+
+For more information, try '--help'.
+",
+ )
+ .with_status(1)
+ .run();
+}
+
+#[cargo_test]
fn chdir_gated() {
let p = project()
.file("Cargo.toml", &basic_bin_manifest("foo"))
@@ -222,6 +269,33 @@ fn cargo_compile_directory_not_cwd() {
}
#[cargo_test]
+fn cargo_compile_with_unsupported_short_unstable_feature_flag() {
+ let p = project()
+ .file("Cargo.toml", &basic_bin_manifest("foo"))
+ .file("src/foo.rs", &main_file(r#""i am foo""#, &[]))
+ .file(".cargo/config.toml", &"")
+ .build();
+
+ p.cargo("-zunstable-options -C foo build")
+ .masquerade_as_nightly_cargo(&["chdir"])
+ .cwd(p.root().parent().unwrap())
+ .with_stderr(
+ "\
+error: unexpected argument '-z' found
+
+ tip: a similar argument exists: '-Z'
+
+Usage: cargo [..][OPTIONS] [COMMAND]
+ cargo [..][OPTIONS] -Zscript <MANIFEST_RS> [ARGS]...
+
+For more information, try '--help'.
+",
+ )
+ .with_status(1)
+ .run();
+}
+
+#[cargo_test]
fn cargo_compile_directory_not_cwd_with_invalid_config() {
let p = project()
.file("Cargo.toml", &basic_bin_manifest("foo"))
@@ -465,7 +539,7 @@ fn cargo_compile_with_forbidden_bin_target_name() {
[ERROR] failed to parse manifest at `[..]`
Caused by:
- the binary target name `build` is forbidden, it conflicts with with cargo's build directory names
+ the binary target name `build` is forbidden, it conflicts with cargo's build directory names
",
)
.run();
@@ -4190,6 +4264,30 @@ fn cargo_build_empty_target() {
}
#[cargo_test]
+fn cargo_build_with_unsupported_short_target_flag() {
+ let p = project()
+ .file("Cargo.toml", &basic_bin_manifest("foo"))
+ .file("src/main.rs", "fn main() {}")
+ .build();
+
+ p.cargo("build -t")
+ .arg("")
+ .with_stderr(
+ "\
+error: unexpected argument '-t' found
+
+ tip: a similar argument exists: '--target'
+
+Usage: cargo[EXE] build [OPTIONS]
+
+For more information, try '--help'.
+",
+ )
+ .with_status(1)
+ .run();
+}
+
+#[cargo_test]
fn build_all_workspace() {
let p = project()
.file(
@@ -4255,6 +4353,43 @@ fn build_all_exclude() {
}
#[cargo_test]
+fn cargo_build_with_unsupported_short_exclude_flag() {
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.1.0"
+
+ [workspace]
+ members = ["bar", "baz"]
+ "#,
+ )
+ .file("src/main.rs", "fn main() {}")
+ .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0"))
+ .file("bar/src/lib.rs", "pub fn bar() {}")
+ .file("baz/Cargo.toml", &basic_manifest("baz", "0.1.0"))
+ .file("baz/src/lib.rs", "pub fn baz() { break_the_build(); }")
+ .build();
+
+ p.cargo("build --workspace -x baz")
+ .with_stderr(
+ "\
+error: unexpected argument '-x' found
+
+ tip: a similar argument exists: '--exclude'
+
+Usage: cargo[EXE] build [OPTIONS]
+
+For more information, try '--help'.
+",
+ )
+ .with_status(1)
+ .run();
+}
+
+#[cargo_test]
fn build_all_exclude_not_found() {
let p = project()
.file(
diff --git a/src/tools/cargo/tests/testsuite/build_script.rs b/src/tools/cargo/tests/testsuite/build_script.rs
index 0ccbb4e27..408ce6457 100644
--- a/src/tools/cargo/tests/testsuite/build_script.rs
+++ b/src/tools/cargo/tests/testsuite/build_script.rs
@@ -1471,6 +1471,7 @@ fn testing_and_such() {
[DOCUMENTING] foo v0.5.0 ([CWD])
[RUNNING] `rustdoc [..]`
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+[GENERATED] [CWD]/target/doc/foo/index.html
",
)
.run();
@@ -3778,8 +3779,8 @@ fn warnings_emitted() {
[COMPILING] foo v0.5.0 ([..])
[RUNNING] `rustc [..]`
[RUNNING] `[..]`
-warning: foo
-warning: bar
+warning: foo@0.5.0: foo
+warning: foo@0.5.0: bar
[RUNNING] `rustc [..]`
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
",
@@ -3816,7 +3817,7 @@ fn warnings_emitted_when_build_script_panics() {
p.cargo("build")
.with_status(101)
.with_stdout("")
- .with_stderr_contains("warning: foo\nwarning: bar")
+ .with_stderr_contains("warning: foo@0.5.0: foo\nwarning: foo@0.5.0: bar")
.run();
}
@@ -3929,8 +3930,8 @@ fn warnings_printed_on_vv() {
[COMPILING] bar v0.1.0
[RUNNING] `[..] rustc [..]`
[RUNNING] `[..]`
-warning: foo
-warning: bar
+warning: bar@0.1.0: foo
+warning: bar@0.1.0: bar
[RUNNING] `[..] rustc [..]`
[COMPILING] foo v0.5.0 ([..])
[RUNNING] `[..] rustc [..]`
diff --git a/src/tools/cargo/tests/testsuite/build_script_env.rs b/src/tools/cargo/tests/testsuite/build_script_env.rs
index df574600c..afa2925f1 100644
--- a/src/tools/cargo/tests/testsuite/build_script_env.rs
+++ b/src/tools/cargo/tests/testsuite/build_script_env.rs
@@ -137,12 +137,12 @@ fn rustc_bootstrap() {
// NOTE: uses RUSTC_BOOTSTRAP so it will be propagated to rustc
// (this matters when tests are being run with a beta or stable cargo)
.env("RUSTC_BOOTSTRAP", "1")
- .with_stderr_contains("warning: Cannot set `RUSTC_BOOTSTRAP=1` [..]")
+ .with_stderr_contains("warning: has-dashes@0.0.1: Cannot set `RUSTC_BOOTSTRAP=1` [..]")
.run();
// RUSTC_BOOTSTRAP set to the name of the library should warn
p.cargo("check")
.env("RUSTC_BOOTSTRAP", "has_dashes")
- .with_stderr_contains("warning: Cannot set `RUSTC_BOOTSTRAP=1` [..]")
+ .with_stderr_contains("warning: has-dashes@0.0.1: Cannot set `RUSTC_BOOTSTRAP=1` [..]")
.run();
// RUSTC_BOOTSTRAP set to some random value should error
p.cargo("check")
@@ -169,7 +169,7 @@ fn rustc_bootstrap() {
// NOTE: uses RUSTC_BOOTSTRAP so it will be propagated to rustc
// (this matters when tests are being run with a beta or stable cargo)
.env("RUSTC_BOOTSTRAP", "1")
- .with_stderr_contains("warning: Cannot set `RUSTC_BOOTSTRAP=1` [..]")
+ .with_stderr_contains("warning: foo@0.0.1: Cannot set `RUSTC_BOOTSTRAP=1` [..]")
.run();
// RUSTC_BOOTSTRAP conditionally set when there's no library should error (regardless of the value)
p.cargo("check")
@@ -181,6 +181,22 @@ fn rustc_bootstrap() {
}
#[cargo_test]
+fn build_script_env_verbose() {
+ let build_rs = r#"
+ fn main() {}
+ "#;
+ let p = project()
+ .file("Cargo.toml", &basic_manifest("verbose-build", "0.0.1"))
+ .file("src/lib.rs", "")
+ .file("build.rs", build_rs)
+ .build();
+
+ p.cargo("check -vv")
+ .with_stderr_contains("[RUNNING] `[..]CARGO=[..]build-script-build`")
+ .run();
+}
+
+#[cargo_test]
#[cfg(target_arch = "x86_64")]
fn build_script_sees_cfg_target_feature() {
let build_rs = r#"
diff --git a/src/tools/cargo/tests/testsuite/build_script_extra_link_arg.rs b/src/tools/cargo/tests/testsuite/build_script_extra_link_arg.rs
index ade262fec..964a55e97 100644
--- a/src/tools/cargo/tests/testsuite/build_script_extra_link_arg.rs
+++ b/src/tools/cargo/tests/testsuite/build_script_extra_link_arg.rs
@@ -204,7 +204,7 @@ fn cdylib_link_arg_transitive() {
[COMPILING] bar v1.0.0 [..]
[RUNNING] `rustc --crate-name build_script_build bar/build.rs [..]
[RUNNING] `[..]build-script-build[..]
-warning: cargo:rustc-link-arg-cdylib was specified in the build script of bar v1.0.0 \
+warning: bar@1.0.0: cargo:rustc-link-arg-cdylib was specified in the build script of bar v1.0.0 \
([ROOT]/foo/bar), but that package does not contain a cdylib target
Allowing this was an unintended change in the 1.50 release, and may become an error in \
diff --git a/src/tools/cargo/tests/testsuite/cache_lock.rs b/src/tools/cargo/tests/testsuite/cache_lock.rs
new file mode 100644
index 000000000..f5b681f3a
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cache_lock.rs
@@ -0,0 +1,304 @@
+//! Tests for `CacheLock`.
+
+use crate::config::ConfigBuilder;
+use cargo::util::cache_lock::{CacheLockMode, CacheLocker};
+use cargo_test_support::paths::{self, CargoPathExt};
+use cargo_test_support::{retry, thread_wait_timeout, threaded_timeout};
+use std::thread::JoinHandle;
+
+/// Helper to verify that it is OK to acquire the given lock (it shouldn't block).
+fn verify_lock_is_ok(mode: CacheLockMode) {
+ let root = paths::root();
+ threaded_timeout(10, move || {
+ let config = ConfigBuilder::new().root(root).build();
+ let locker = CacheLocker::new();
+ // This would block if it is held.
+ let _lock = locker.lock(&config, mode).unwrap();
+ assert!(locker.is_locked(mode));
+ });
+}
+
+/// Helper to acquire two locks from the same locker.
+fn a_b_nested(a: CacheLockMode, b: CacheLockMode) {
+ let config = ConfigBuilder::new().build();
+ let locker = CacheLocker::new();
+ let lock1 = locker.lock(&config, a).unwrap();
+ assert!(locker.is_locked(a));
+ let lock2 = locker.lock(&config, b).unwrap();
+ assert!(locker.is_locked(b));
+ drop(lock2);
+ drop(lock1);
+ // Verify locks were unlocked.
+ verify_lock_is_ok(CacheLockMode::Shared);
+ verify_lock_is_ok(CacheLockMode::DownloadExclusive);
+ verify_lock_is_ok(CacheLockMode::MutateExclusive);
+}
+
+/// Helper to acquire two locks from separate lockers, verifying that they
+/// don't block each other.
+fn a_then_b_separate_not_blocked(a: CacheLockMode, b: CacheLockMode, verify: CacheLockMode) {
+ let config = ConfigBuilder::new().build();
+ let locker1 = CacheLocker::new();
+ let lock1 = locker1.lock(&config, a).unwrap();
+ assert!(locker1.is_locked(a));
+ let locker2 = CacheLocker::new();
+ let lock2 = locker2.lock(&config, b).unwrap();
+ assert!(locker2.is_locked(b));
+ let thread = verify_lock_would_block(verify);
+ // Unblock the thread.
+ drop(lock1);
+ drop(lock2);
+ // Verify the thread is unblocked.
+ thread_wait_timeout::<()>(100, thread);
+}
+
+/// Helper to acquire two locks from separate lockers, verifying that the
+/// second one blocks.
+fn a_then_b_separate_blocked(a: CacheLockMode, b: CacheLockMode) {
+ let config = ConfigBuilder::new().build();
+ let locker = CacheLocker::new();
+ let lock = locker.lock(&config, a).unwrap();
+ assert!(locker.is_locked(a));
+ let thread = verify_lock_would_block(b);
+ // Unblock the thread.
+ drop(lock);
+ // Verify the thread is unblocked.
+ thread_wait_timeout::<()>(100, thread);
+}
+
+/// Helper to verify that acquiring the given mode would block.
+///
+/// Always call `thread_wait_timeout` on the result.
+#[must_use]
+fn verify_lock_would_block(mode: CacheLockMode) -> JoinHandle<()> {
+ let root = paths::root();
+ // Spawn a thread that will block on the lock.
+ let thread = std::thread::spawn(move || {
+ let config = ConfigBuilder::new().root(root).build();
+ let locker2 = CacheLocker::new();
+ let lock2 = locker2.lock(&config, mode).unwrap();
+ assert!(locker2.is_locked(mode));
+ drop(lock2);
+ });
+ // Verify that it blocked.
+ retry(100, || {
+ if let Ok(s) = std::fs::read_to_string(paths::root().join("shell.out")) {
+ if s.trim().starts_with("Blocking waiting for file lock on") {
+ return Some(());
+ } else {
+ eprintln!("unexpected output: {s}");
+ // Try again, it might have been partially written.
+ }
+ }
+ None
+ });
+ thread
+}
+
+#[test]
+fn new_is_unlocked() {
+ let locker = CacheLocker::new();
+ assert!(!locker.is_locked(CacheLockMode::Shared));
+ assert!(!locker.is_locked(CacheLockMode::DownloadExclusive));
+ assert!(!locker.is_locked(CacheLockMode::MutateExclusive));
+}
+
+#[cargo_test]
+fn multiple_shared() {
+ // Test that two nested shared locks from the same locker are safe to acquire.
+ a_b_nested(CacheLockMode::Shared, CacheLockMode::Shared);
+}
+
+#[cargo_test]
+fn multiple_shared_separate() {
+ // Test that two independent shared locks are safe to acquire at the same time.
+ a_then_b_separate_not_blocked(
+ CacheLockMode::Shared,
+ CacheLockMode::Shared,
+ CacheLockMode::MutateExclusive,
+ );
+}
+
+#[cargo_test]
+fn multiple_download() {
+ // That that two nested download locks from the same locker are safe to acquire.
+ a_b_nested(
+ CacheLockMode::DownloadExclusive,
+ CacheLockMode::DownloadExclusive,
+ );
+}
+
+#[cargo_test]
+fn multiple_mutate() {
+ // That that two nested mutate locks from the same locker are safe to acquire.
+ a_b_nested(
+ CacheLockMode::MutateExclusive,
+ CacheLockMode::MutateExclusive,
+ );
+}
+
+#[cargo_test]
+#[should_panic(expected = "lock is not allowed")]
+fn download_then_shared() {
+ // This sequence is not supported.
+ a_b_nested(CacheLockMode::DownloadExclusive, CacheLockMode::Shared);
+}
+
+#[cargo_test]
+#[should_panic(expected = "lock upgrade from shared to exclusive not supported")]
+fn shared_then_mutate() {
+ // This sequence is not supported.
+ a_b_nested(CacheLockMode::Shared, CacheLockMode::MutateExclusive);
+}
+
+#[cargo_test]
+fn shared_then_download() {
+ a_b_nested(CacheLockMode::Shared, CacheLockMode::DownloadExclusive);
+ // Verify drop actually unlocked.
+ verify_lock_is_ok(CacheLockMode::DownloadExclusive);
+ verify_lock_is_ok(CacheLockMode::MutateExclusive);
+}
+
+#[cargo_test]
+fn mutate_then_shared() {
+ a_b_nested(CacheLockMode::MutateExclusive, CacheLockMode::Shared);
+ // Verify drop actually unlocked.
+ verify_lock_is_ok(CacheLockMode::MutateExclusive);
+}
+
+#[cargo_test]
+fn download_then_mutate() {
+ a_b_nested(
+ CacheLockMode::DownloadExclusive,
+ CacheLockMode::MutateExclusive,
+ );
+ // Verify drop actually unlocked.
+ verify_lock_is_ok(CacheLockMode::DownloadExclusive);
+ verify_lock_is_ok(CacheLockMode::MutateExclusive);
+}
+
+#[cargo_test]
+fn mutate_then_download() {
+ a_b_nested(
+ CacheLockMode::MutateExclusive,
+ CacheLockMode::DownloadExclusive,
+ );
+ // Verify drop actually unlocked.
+ verify_lock_is_ok(CacheLockMode::MutateExclusive);
+ verify_lock_is_ok(CacheLockMode::DownloadExclusive);
+}
+
+#[cargo_test]
+fn readonly() {
+ // In a permission denied situation, it should still allow a lock. It just
+ // silently behaves as-if it was locked.
+ let cargo_home = paths::home().join(".cargo");
+ std::fs::create_dir_all(&cargo_home).unwrap();
+ let mut perms = std::fs::metadata(&cargo_home).unwrap().permissions();
+ perms.set_readonly(true);
+ std::fs::set_permissions(&cargo_home, perms).unwrap();
+ let config = ConfigBuilder::new().build();
+ let locker = CacheLocker::new();
+ for mode in [
+ CacheLockMode::Shared,
+ CacheLockMode::DownloadExclusive,
+ CacheLockMode::MutateExclusive,
+ ] {
+ let _lock1 = locker.lock(&config, mode).unwrap();
+ // Make sure it can recursively acquire the lock, too.
+ let _lock2 = locker.lock(&config, mode).unwrap();
+ }
+}
+
+#[cargo_test]
+fn download_then_shared_separate() {
+ a_then_b_separate_not_blocked(
+ CacheLockMode::DownloadExclusive,
+ CacheLockMode::Shared,
+ CacheLockMode::MutateExclusive,
+ );
+}
+
+#[cargo_test]
+fn shared_then_download_separate() {
+ a_then_b_separate_not_blocked(
+ CacheLockMode::Shared,
+ CacheLockMode::DownloadExclusive,
+ CacheLockMode::MutateExclusive,
+ );
+}
+
+#[cargo_test]
+fn multiple_download_separate() {
+ // Test that with two independent download locks, the second blocks until
+ // the first is released.
+ a_then_b_separate_blocked(
+ CacheLockMode::DownloadExclusive,
+ CacheLockMode::DownloadExclusive,
+ );
+}
+
+#[cargo_test]
+fn multiple_mutate_separate() {
+ // Test that with two independent mutate locks, the second blocks until
+ // the first is released.
+ a_then_b_separate_blocked(
+ CacheLockMode::MutateExclusive,
+ CacheLockMode::MutateExclusive,
+ );
+}
+
+#[cargo_test]
+fn shared_then_mutate_separate() {
+ a_then_b_separate_blocked(CacheLockMode::Shared, CacheLockMode::MutateExclusive);
+}
+
+#[cargo_test]
+fn download_then_mutate_separate() {
+ a_then_b_separate_blocked(
+ CacheLockMode::DownloadExclusive,
+ CacheLockMode::MutateExclusive,
+ );
+}
+
+#[cargo_test]
+fn mutate_then_download_separate() {
+ a_then_b_separate_blocked(
+ CacheLockMode::MutateExclusive,
+ CacheLockMode::DownloadExclusive,
+ );
+}
+
+#[cargo_test]
+fn mutate_then_shared_separate() {
+ a_then_b_separate_blocked(CacheLockMode::MutateExclusive, CacheLockMode::Shared);
+}
+
+#[cargo_test(ignore_windows = "no method to prevent creating or locking a file")]
+fn mutate_err_is_atomic() {
+ // Verifies that when getting a mutate lock, that if the first lock
+ // succeeds, but the second one fails, that the first lock is released.
+ let config = ConfigBuilder::new().build();
+ let locker = CacheLocker::new();
+ let cargo_home = config.home().as_path_unlocked();
+ let cache_path = cargo_home.join(".package-cache");
+ // This is a hacky way to force an error acquiring the download lock. By
+ // making it a directory, it is unable to open it.
+ // TODO: Unfortunately this doesn't work on Windows. I don't have any
+ // ideas on how to simulate an error on Windows.
+ cache_path.mkdir_p();
+ match locker.lock(&config, CacheLockMode::MutateExclusive) {
+ Ok(_) => panic!("did not expect lock to succeed"),
+ Err(e) => {
+ let msg = format!("{e:?}");
+ assert!(msg.contains("failed to open:"), "{msg}");
+ }
+ }
+ assert!(!locker.is_locked(CacheLockMode::MutateExclusive));
+ assert!(!locker.is_locked(CacheLockMode::DownloadExclusive));
+ assert!(!locker.is_locked(CacheLockMode::Shared));
+ cache_path.rm_rf();
+ verify_lock_is_ok(CacheLockMode::DownloadExclusive);
+ verify_lock_is_ok(CacheLockMode::Shared);
+ verify_lock_is_ok(CacheLockMode::MutateExclusive);
+}
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/mod.rs
index 8c03b30dc..e8633b0c4 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/mod.rs
@@ -99,6 +99,7 @@ mod path_dev;
mod path_inferred_name;
mod path_inferred_name_conflicts_full_feature;
mod path_normalized_name;
+mod preserve_dep_std_table;
mod preserve_features_table;
mod preserve_sorted;
mod preserve_unsorted;
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/preserve_dep_std_table/in/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_add/preserve_dep_std_table/in/Cargo.toml
new file mode 100644
index 000000000..aa8584ff4
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_add/preserve_dep_std_table/in/Cargo.toml
@@ -0,0 +1,14 @@
+[package]
+name = "xxx"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies.your-face]
+# Leading version
+version = "99999.0.0" # Trailing version
+# Leading optional
+optional = true # Trailing optional
+# Leading features
+features = [] # Trailing features
diff --git a/src/tools/cargo/tests/testsuite/cargo_init/empty_dir/.keep b/src/tools/cargo/tests/testsuite/cargo_add/preserve_dep_std_table/in/src/lib.rs
index e69de29bb..e69de29bb 100644
--- a/src/tools/cargo/tests/testsuite/cargo_init/empty_dir/.keep
+++ b/src/tools/cargo/tests/testsuite/cargo_add/preserve_dep_std_table/in/src/lib.rs
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/preserve_dep_std_table/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/preserve_dep_std_table/mod.rs
new file mode 100644
index 000000000..1998fa742
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_add/preserve_dep_std_table/mod.rs
@@ -0,0 +1,31 @@
+use cargo_test_support::compare::assert_ui;
+use cargo_test_support::prelude::*;
+use cargo_test_support::Project;
+
+use cargo_test_support::curr_dir;
+
+#[cargo_test]
+fn case() {
+ cargo_test_support::registry::init();
+ cargo_test_support::registry::Package::new("your-face", "99999.0.0+my-package")
+ .feature("nose", &[])
+ .feature("mouth", &[])
+ .feature("eyes", &[])
+ .feature("ears", &[])
+ .publish();
+
+ let project = Project::from_template(curr_dir!().join("in"));
+ let project_root = project.root();
+ let cwd = &project_root;
+
+ snapbox::cmd::Command::cargo_ui()
+ .arg("add")
+ .arg_line("your-face --no-optional")
+ .current_dir(cwd)
+ .assert()
+ .success()
+ .stdout_matches_path(curr_dir!().join("stdout.log"))
+ .stderr_matches_path(curr_dir!().join("stderr.log"));
+
+ assert_ui().subset_matches(curr_dir!().join("out"), &project_root);
+}
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/preserve_dep_std_table/out/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_add/preserve_dep_std_table/out/Cargo.toml
new file mode 100644
index 000000000..76e50ce37
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_add/preserve_dep_std_table/out/Cargo.toml
@@ -0,0 +1,12 @@
+[package]
+name = "xxx"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies.your-face]
+# Leading version
+version = "99999.0.0" # Trailing version
+# Leading features
+features = [] # Trailing features
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/preserve_dep_std_table/stderr.log b/src/tools/cargo/tests/testsuite/cargo_add/preserve_dep_std_table/stderr.log
new file mode 100644
index 000000000..796b9601b
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_add/preserve_dep_std_table/stderr.log
@@ -0,0 +1,7 @@
+ Updating `dummy-registry` index
+ Adding your-face v99999.0.0 to dependencies.
+ Features:
+ - ears
+ - eyes
+ - mouth
+ - nose
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/preserve_dep_std_table/stdout.log b/src/tools/cargo/tests/testsuite/cargo_add/preserve_dep_std_table/stdout.log
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_add/preserve_dep_std_table/stdout.log
diff --git a/src/tools/cargo/tests/testsuite/cargo_bench/help/stdout.log b/src/tools/cargo/tests/testsuite/cargo_bench/help/stdout.log
index 430d8be42..95546b4a3 100644
--- a/src/tools/cargo/tests/testsuite/cargo_bench/help/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo_bench/help/stdout.log
@@ -1,10 +1,10 @@
Execute all benchmarks of a local package
-Usage: cargo[EXE] bench [OPTIONS] [BENCHNAME] [-- [args]...]
+Usage: cargo[EXE] bench [OPTIONS] [BENCHNAME] [-- [ARGS]...]
Arguments:
[BENCHNAME] If specified, only run benches containing this string in their names
- [args]... Arguments for the bench binary
+ [ARGS]... Arguments for the bench binary
Options:
--no-run Compile, but don't run benchmarks
@@ -31,9 +31,9 @@ Target Selection:
--bin [<NAME>] Benchmark only the specified binary
--examples Benchmark all examples
--example [<NAME>] Benchmark only the specified example
- --tests Benchmark all tests
+ --tests Benchmark all test targets
--test [<NAME>] Benchmark only the specified test target
- --benches Benchmark all benches
+ --benches Benchmark all bench targets
--bench [<NAME>] Benchmark only the specified bench target
--all-targets Benchmark all targets
diff --git a/src/tools/cargo/tests/testsuite/cargo_bench/no_keep_going/stderr.log b/src/tools/cargo/tests/testsuite/cargo_bench/no_keep_going/stderr.log
index 7b94abbc4..daaa8f093 100644
--- a/src/tools/cargo/tests/testsuite/cargo_bench/no_keep_going/stderr.log
+++ b/src/tools/cargo/tests/testsuite/cargo_bench/no_keep_going/stderr.log
@@ -2,6 +2,6 @@ error: unexpected argument '--keep-going' found
tip: use `--no-fail-fast` to run as many tests as possible regardless of failure
-Usage: cargo[EXE] bench [OPTIONS] [BENCHNAME] [-- [args]...]
+Usage: cargo[EXE] bench [OPTIONS] [BENCHNAME] [-- [ARGS]...]
For more information, try '--help'.
diff --git a/src/tools/cargo/tests/testsuite/cargo_build/help/stdout.log b/src/tools/cargo/tests/testsuite/cargo_build/help/stdout.log
index 56b934cd1..58b12cdcd 100644
--- a/src/tools/cargo/tests/testsuite/cargo_build/help/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo_build/help/stdout.log
@@ -26,9 +26,9 @@ Target Selection:
--bin [<NAME>] Build only the specified binary
--examples Build all examples
--example [<NAME>] Build only the specified example
- --tests Build all tests
+ --tests Build all test targets
--test [<NAME>] Build only the specified test target
- --benches Build all benches
+ --benches Build all bench targets
--bench [<NAME>] Build only the specified bench target
--all-targets Build all targets
diff --git a/src/tools/cargo/tests/testsuite/cargo_check/help/stdout.log b/src/tools/cargo/tests/testsuite/cargo_check/help/stdout.log
index 92d44a6de..bbf090d1d 100644
--- a/src/tools/cargo/tests/testsuite/cargo_check/help/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo_check/help/stdout.log
@@ -26,9 +26,9 @@ Target Selection:
--bin [<NAME>] Check only the specified binary
--examples Check all examples
--example [<NAME>] Check only the specified example
- --tests Check all tests
+ --tests Check all test targets
--test [<NAME>] Check only the specified test target
- --benches Check all benches
+ --benches Check all bench targets
--bench [<NAME>] Check only the specified bench target
--all-targets Check all targets
diff --git a/src/tools/cargo/tests/testsuite/cargo_command.rs b/src/tools/cargo/tests/testsuite/cargo_command.rs
index 62869387f..80885805b 100644
--- a/src/tools/cargo/tests/testsuite/cargo_command.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_command.rs
@@ -294,7 +294,9 @@ fn find_closest_dont_correct_nonsense() {
"\
[ERROR] no such command: `there-is-no-way-that-there-is-a-command-close-to-this`
-<tab>View all installed commands with `cargo --list`",
+<tab>View all installed commands with `cargo --list`
+<tab>Find a package to install `there-is-no-way-that-there-is-a-command-close-to-this` with `cargo search cargo-there-is-no-way-that-there-is-a-command-close-to-this`
+",
)
.run();
}
@@ -307,7 +309,9 @@ fn displays_subcommand_on_error() {
"\
[ERROR] no such command: `invalid-command`
-<tab>View all installed commands with `cargo --list`",
+<tab>View all installed commands with `cargo --list`
+<tab>Find a package to install `invalid-command` with `cargo search cargo-invalid-command`
+",
)
.run();
}
@@ -529,7 +533,9 @@ error: no such command: `bluid`
<tab>Did you mean `build`?
-<tab>View all installed commands with `cargo --list`",
+<tab>View all installed commands with `cargo --list`
+<tab>Find a package to install `bluid` with `cargo search cargo-bluid`
+",
)
.run();
}
diff --git a/src/tools/cargo/tests/testsuite/cargo_features.rs b/src/tools/cargo/tests/testsuite/cargo_features.rs
index cf7ef0190..d319ed686 100644
--- a/src/tools/cargo/tests/testsuite/cargo_features.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_features.rs
@@ -594,7 +594,13 @@ fn z_flags_rejected() {
p.cargo("check -Zarg")
.masquerade_as_nightly_cargo(&["test-dummy-unstable"])
.with_status(101)
- .with_stderr("error: unknown `-Z` flag specified: arg")
+ .with_stderr(
+ r#"error: unknown `-Z` flag specified: arg
+
+For available unstable features, see https://doc.rust-lang.org/nightly/cargo/reference/unstable.html
+If you intended to use an unstable rustc feature, try setting `RUSTFLAGS="-Zarg"`
+"#,
+ )
.run();
p.cargo("check -Zprint-im-a-teapot")
diff --git a/src/tools/cargo/tests/testsuite/cargo_fix/help/stdout.log b/src/tools/cargo/tests/testsuite/cargo_fix/help/stdout.log
index dbbd11b77..3e8b1427f 100644
--- a/src/tools/cargo/tests/testsuite/cargo_fix/help/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo_fix/help/stdout.log
@@ -31,9 +31,9 @@ Target Selection:
--bin [<NAME>] Fix only the specified binary
--examples Fix all examples
--example [<NAME>] Fix only the specified example
- --tests Fix all tests
+ --tests Fix all test targets
--test [<NAME>] Fix only the specified test target
- --benches Fix all benches
+ --benches Fix all bench targets
--bench [<NAME>] Fix only the specified bench target
--all-targets Fix all targets (default)
diff --git a/src/tools/cargo/tests/testsuite/cargo_init/help/stdout.log b/src/tools/cargo/tests/testsuite/cargo_init/help/stdout.log
index 0eb4c976b..588b45ccf 100644
--- a/src/tools/cargo/tests/testsuite/cargo_init/help/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo_init/help/stdout.log
@@ -1,9 +1,9 @@
Create a new cargo package in an existing directory
-Usage: cargo[EXE] init [OPTIONS] [path]
+Usage: cargo[EXE] init [OPTIONS] [PATH]
Arguments:
- [path] [default: .]
+ [PATH] [default: .]
Options:
--vcs <VCS> Initialize a new repository for the given version control system,
@@ -12,7 +12,7 @@ Options:
--bin Use a binary (application) template [default]
--lib Use a library template
--edition <YEAR> Edition to set for the crate generated [possible values: 2015, 2018,
- 2021]
+ 2021, 2024]
--name <NAME> Set the resulting package name, defaults to the directory name
--registry <REGISTRY> Registry to use
-q, --quiet Do not print cargo log messages
diff --git a/src/tools/cargo/tests/testsuite/cargo_init/mod.rs b/src/tools/cargo/tests/testsuite/cargo_init/mod.rs
index a1988a06a..0b397111e 100644
--- a/src/tools/cargo/tests/testsuite/cargo_init/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_init/mod.rs
@@ -42,3 +42,4 @@ mod simple_hg_ignore_exists;
mod simple_lib;
mod unknown_flags;
mod with_argument;
+mod workspace_add_member;
diff --git a/src/tools/cargo/tests/testsuite/cargo_init/unknown_flags/stderr.log b/src/tools/cargo/tests/testsuite/cargo_init/unknown_flags/stderr.log
index 980e8acd8..04a3c3ff0 100644
--- a/src/tools/cargo/tests/testsuite/cargo_init/unknown_flags/stderr.log
+++ b/src/tools/cargo/tests/testsuite/cargo_init/unknown_flags/stderr.log
@@ -2,6 +2,6 @@ error: unexpected argument '--flag' found
tip: to pass '--flag' as a value, use '-- --flag'
-Usage: cargo[EXE] init <path>
+Usage: cargo[EXE] init <PATH>
For more information, try '--help'.
diff --git a/src/tools/cargo/tests/testsuite/cargo_init/workspace_add_member/in/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_init/workspace_add_member/in/Cargo.toml
new file mode 100644
index 000000000..61bdb9cbf
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_init/workspace_add_member/in/Cargo.toml
@@ -0,0 +1,2 @@
+[workspace]
+resolver = "2"
diff --git a/src/tools/cargo/tests/testsuite/cargo_init/workspace_add_member/in/crates/foo/.keep b/src/tools/cargo/tests/testsuite/cargo_init/workspace_add_member/in/crates/foo/.keep
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_init/workspace_add_member/in/crates/foo/.keep
diff --git a/src/tools/cargo/tests/testsuite/cargo_init/workspace_add_member/mod.rs b/src/tools/cargo/tests/testsuite/cargo_init/workspace_add_member/mod.rs
new file mode 100644
index 000000000..87e2af0e5
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_init/workspace_add_member/mod.rs
@@ -0,0 +1,21 @@
+use cargo_test_support::compare::assert_ui;
+use cargo_test_support::prelude::*;
+use cargo_test_support::Project;
+
+use cargo_test_support::curr_dir;
+
+#[cargo_test]
+fn case() {
+ let project = Project::from_template(curr_dir!().join("in"));
+ let project_root = &project.root();
+
+ snapbox::cmd::Command::cargo_ui()
+ .arg_line("init --bin --vcs none")
+ .current_dir(project_root.join("crates").join("foo"))
+ .assert()
+ .success()
+ .stdout_matches_path(curr_dir!().join("stdout.log"))
+ .stderr_matches_path(curr_dir!().join("stderr.log"));
+
+ assert_ui().subset_matches(curr_dir!().join("out"), &project_root);
+}
diff --git a/src/tools/cargo/tests/testsuite/cargo_init/workspace_add_member/out/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_init/workspace_add_member/out/Cargo.toml
new file mode 100644
index 000000000..18a4e7cf2
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_init/workspace_add_member/out/Cargo.toml
@@ -0,0 +1,3 @@
+[workspace]
+resolver = "2"
+members = ["crates/foo"]
diff --git a/src/tools/cargo/tests/testsuite/cargo_init/workspace_add_member/out/crates/foo/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_init/workspace_add_member/out/crates/foo/Cargo.toml
new file mode 100644
index 000000000..1d9cfe317
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_init/workspace_add_member/out/crates/foo/Cargo.toml
@@ -0,0 +1,8 @@
+[package]
+name = "foo"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
diff --git a/src/tools/cargo/tests/testsuite/cargo_init/workspace_add_member/out/crates/foo/src/main.rs b/src/tools/cargo/tests/testsuite/cargo_init/workspace_add_member/out/crates/foo/src/main.rs
new file mode 100644
index 000000000..e7a11a969
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_init/workspace_add_member/out/crates/foo/src/main.rs
@@ -0,0 +1,3 @@
+fn main() {
+ println!("Hello, world!");
+}
diff --git a/src/tools/cargo/tests/testsuite/cargo_init/workspace_add_member/stderr.log b/src/tools/cargo/tests/testsuite/cargo_init/workspace_add_member/stderr.log
new file mode 100644
index 000000000..3847e4e4a
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_init/workspace_add_member/stderr.log
@@ -0,0 +1 @@
+ Created binary (application) package
diff --git a/src/tools/cargo/tests/testsuite/cargo_init/workspace_add_member/stdout.log b/src/tools/cargo/tests/testsuite/cargo_init/workspace_add_member/stdout.log
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_init/workspace_add_member/stdout.log
diff --git a/src/tools/cargo/tests/testsuite/cargo_install/help/stdout.log b/src/tools/cargo/tests/testsuite/cargo_install/help/stdout.log
index 2267c5f6b..5e3458d37 100644
--- a/src/tools/cargo/tests/testsuite/cargo_install/help/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo_install/help/stdout.log
@@ -1,9 +1,9 @@
Install a Rust binary. Default location is $HOME/.cargo/bin
-Usage: cargo[EXE] install [OPTIONS] [crate]...
+Usage: cargo[EXE] install [OPTIONS] [CRATE[@<VER>]]...
Arguments:
- [crate]...
+ [CRATE[@<VER>]]... Select the package from the given source
Options:
--version <VERSION> Specify a version to install
diff --git a/src/tools/cargo/tests/testsuite/cargo_login/help/stdout.log b/src/tools/cargo/tests/testsuite/cargo_login/help/stdout.log
index fd0f3eb3d..e0d5e7e69 100644
--- a/src/tools/cargo/tests/testsuite/cargo_login/help/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo_login/help/stdout.log
@@ -1,9 +1,9 @@
Log in to a registry.
-Usage: cargo[EXE] login [OPTIONS] [token] [-- [args]...]
+Usage: cargo[EXE] login [OPTIONS] [TOKEN] [-- [args]...]
Arguments:
- [token]
+ [TOKEN]
[args]... Additional arguments for the credential provider
Options:
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_previous_items/in/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_previous_items/in/Cargo.toml
new file mode 100644
index 000000000..0614e21f7
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_previous_items/in/Cargo.toml
@@ -0,0 +1,5 @@
+[workspace]
+resolver = "2"
+
+[workspace.package]
+authors = ["Rustaceans"]
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_previous_items/mod.rs b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_previous_items/mod.rs
new file mode 100644
index 000000000..9b9642468
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_previous_items/mod.rs
@@ -0,0 +1,22 @@
+use cargo_test_support::compare::assert_ui;
+use cargo_test_support::curr_dir;
+use cargo_test_support::CargoCommand;
+use cargo_test_support::Project;
+
+#[cargo_test]
+fn case() {
+ let project = Project::from_template(curr_dir!().join("in"));
+ let project_root = project.root();
+ let cwd = &project_root;
+
+ snapbox::cmd::Command::cargo_ui()
+ .arg("new")
+ .args(["crates/foo"])
+ .current_dir(cwd)
+ .assert()
+ .success()
+ .stdout_matches_path(curr_dir!().join("stdout.log"))
+ .stderr_matches_path(curr_dir!().join("stderr.log"));
+
+ assert_ui().subset_matches(curr_dir!().join("out"), &project_root);
+}
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_previous_items/out/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_previous_items/out/Cargo.toml
new file mode 100644
index 000000000..8df793a4f
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_previous_items/out/Cargo.toml
@@ -0,0 +1,6 @@
+[workspace]
+resolver = "2"
+members = ["crates/foo"]
+
+[workspace.package]
+authors = ["Rustaceans"]
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_previous_items/out/crates/foo/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_previous_items/out/crates/foo/Cargo.toml
new file mode 100644
index 000000000..d97480c7c
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_previous_items/out/crates/foo/Cargo.toml
@@ -0,0 +1,9 @@
+[package]
+name = "foo"
+version = "0.1.0"
+edition = "2021"
+authors.workspace = true
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_previous_items/out/crates/foo/src/main.rs b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_previous_items/out/crates/foo/src/main.rs
new file mode 100644
index 000000000..e7a11a969
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_previous_items/out/crates/foo/src/main.rs
@@ -0,0 +1,3 @@
+fn main() {
+ println!("Hello, world!");
+}
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_previous_items/stderr.log b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_previous_items/stderr.log
new file mode 100644
index 000000000..90150cdf5
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_previous_items/stderr.log
@@ -0,0 +1 @@
+ Created binary (application) `crates/foo` package
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_previous_items/stdout.log b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_previous_items/stdout.log
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_previous_items/stdout.log
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_sorted/in/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_sorted/in/Cargo.toml
new file mode 100644
index 000000000..1f3dfe4de
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_sorted/in/Cargo.toml
@@ -0,0 +1,3 @@
+[workspace]
+resolver = "2"
+members = ["crates/bar", "crates/qux"]
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_sorted/in/crates/bar/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_sorted/in/crates/bar/Cargo.toml
new file mode 100644
index 000000000..825efac34
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_sorted/in/crates/bar/Cargo.toml
@@ -0,0 +1,8 @@
+[package]
+name = "bar"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_sorted/in/crates/bar/src/main.rs b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_sorted/in/crates/bar/src/main.rs
new file mode 100644
index 000000000..e7a11a969
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_sorted/in/crates/bar/src/main.rs
@@ -0,0 +1,3 @@
+fn main() {
+ println!("Hello, world!");
+}
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_sorted/in/crates/qux/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_sorted/in/crates/qux/Cargo.toml
new file mode 100644
index 000000000..30a9d52b2
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_sorted/in/crates/qux/Cargo.toml
@@ -0,0 +1,8 @@
+[package]
+name = "qux"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_sorted/in/crates/qux/src/main.rs b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_sorted/in/crates/qux/src/main.rs
new file mode 100644
index 000000000..e7a11a969
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_sorted/in/crates/qux/src/main.rs
@@ -0,0 +1,3 @@
+fn main() {
+ println!("Hello, world!");
+}
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_sorted/mod.rs b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_sorted/mod.rs
new file mode 100644
index 000000000..9b9642468
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_sorted/mod.rs
@@ -0,0 +1,22 @@
+use cargo_test_support::compare::assert_ui;
+use cargo_test_support::curr_dir;
+use cargo_test_support::CargoCommand;
+use cargo_test_support::Project;
+
+#[cargo_test]
+fn case() {
+ let project = Project::from_template(curr_dir!().join("in"));
+ let project_root = project.root();
+ let cwd = &project_root;
+
+ snapbox::cmd::Command::cargo_ui()
+ .arg("new")
+ .args(["crates/foo"])
+ .current_dir(cwd)
+ .assert()
+ .success()
+ .stdout_matches_path(curr_dir!().join("stdout.log"))
+ .stderr_matches_path(curr_dir!().join("stderr.log"));
+
+ assert_ui().subset_matches(curr_dir!().join("out"), &project_root);
+}
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_sorted/out/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_sorted/out/Cargo.toml
new file mode 100644
index 000000000..cf27071a9
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_sorted/out/Cargo.toml
@@ -0,0 +1,3 @@
+[workspace]
+resolver = "2"
+members = ["crates/bar", "crates/foo", "crates/qux"]
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_sorted/out/crates/foo/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_sorted/out/crates/foo/Cargo.toml
new file mode 100644
index 000000000..1d9cfe317
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_sorted/out/crates/foo/Cargo.toml
@@ -0,0 +1,8 @@
+[package]
+name = "foo"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_sorted/out/crates/foo/src/main.rs b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_sorted/out/crates/foo/src/main.rs
new file mode 100644
index 000000000..e7a11a969
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_sorted/out/crates/foo/src/main.rs
@@ -0,0 +1,3 @@
+fn main() {
+ println!("Hello, world!");
+}
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_sorted/stderr.log b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_sorted/stderr.log
new file mode 100644
index 000000000..90150cdf5
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_sorted/stderr.log
@@ -0,0 +1 @@
+ Created binary (application) `crates/foo` package
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_sorted/stdout.log b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_sorted/stdout.log
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_sorted/stdout.log
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_absolute_package_path/in/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_absolute_package_path/in/Cargo.toml
new file mode 100644
index 000000000..226fd80bc
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_absolute_package_path/in/Cargo.toml
@@ -0,0 +1,3 @@
+[workspace]
+resolver = "2"
+members = []
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_absolute_package_path/mod.rs b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_absolute_package_path/mod.rs
new file mode 100644
index 000000000..8bf91be45
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_absolute_package_path/mod.rs
@@ -0,0 +1,23 @@
+use cargo_test_support::compare::assert_ui;
+use cargo_test_support::curr_dir;
+use cargo_test_support::CargoCommand;
+use cargo_test_support::Project;
+
+#[cargo_test]
+fn case() {
+ let project = Project::from_template(curr_dir!().join("in"));
+ let project_root = project.root();
+ let cwd = &project_root;
+ let package_path = cwd.join("crates").join("foo");
+
+ snapbox::cmd::Command::cargo_ui()
+ .arg("new")
+ .args([package_path])
+ .current_dir(cwd)
+ .assert()
+ .success()
+ .stdout_matches_path(curr_dir!().join("stdout.log"))
+ .stderr_matches_path(curr_dir!().join("stderr.log"));
+
+ assert_ui().subset_matches(curr_dir!().join("out"), &project_root);
+}
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_absolute_package_path/out/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_absolute_package_path/out/Cargo.toml
new file mode 100644
index 000000000..18a4e7cf2
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_absolute_package_path/out/Cargo.toml
@@ -0,0 +1,3 @@
+[workspace]
+resolver = "2"
+members = ["crates/foo"]
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_absolute_package_path/out/crates/foo/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_absolute_package_path/out/crates/foo/Cargo.toml
new file mode 100644
index 000000000..1d9cfe317
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_absolute_package_path/out/crates/foo/Cargo.toml
@@ -0,0 +1,8 @@
+[package]
+name = "foo"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_absolute_package_path/out/crates/foo/src/main.rs b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_absolute_package_path/out/crates/foo/src/main.rs
new file mode 100644
index 000000000..e7a11a969
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_absolute_package_path/out/crates/foo/src/main.rs
@@ -0,0 +1,3 @@
+fn main() {
+ println!("Hello, world!");
+}
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_absolute_package_path/stderr.log b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_absolute_package_path/stderr.log
new file mode 100644
index 000000000..c93b25acb
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_absolute_package_path/stderr.log
@@ -0,0 +1 @@
+ Created binary (application) `[ROOT]/case/crates/foo` package
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_absolute_package_path/stdout.log b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_absolute_package_path/stdout.log
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_absolute_package_path/stdout.log
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_empty_members/in/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_empty_members/in/Cargo.toml
new file mode 100644
index 000000000..226fd80bc
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_empty_members/in/Cargo.toml
@@ -0,0 +1,3 @@
+[workspace]
+resolver = "2"
+members = []
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_empty_members/mod.rs b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_empty_members/mod.rs
new file mode 100644
index 000000000..9b9642468
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_empty_members/mod.rs
@@ -0,0 +1,22 @@
+use cargo_test_support::compare::assert_ui;
+use cargo_test_support::curr_dir;
+use cargo_test_support::CargoCommand;
+use cargo_test_support::Project;
+
+#[cargo_test]
+fn case() {
+ let project = Project::from_template(curr_dir!().join("in"));
+ let project_root = project.root();
+ let cwd = &project_root;
+
+ snapbox::cmd::Command::cargo_ui()
+ .arg("new")
+ .args(["crates/foo"])
+ .current_dir(cwd)
+ .assert()
+ .success()
+ .stdout_matches_path(curr_dir!().join("stdout.log"))
+ .stderr_matches_path(curr_dir!().join("stderr.log"));
+
+ assert_ui().subset_matches(curr_dir!().join("out"), &project_root);
+}
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_empty_members/out/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_empty_members/out/Cargo.toml
new file mode 100644
index 000000000..18a4e7cf2
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_empty_members/out/Cargo.toml
@@ -0,0 +1,3 @@
+[workspace]
+resolver = "2"
+members = ["crates/foo"]
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_empty_members/out/crates/foo/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_empty_members/out/crates/foo/Cargo.toml
new file mode 100644
index 000000000..1d9cfe317
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_empty_members/out/crates/foo/Cargo.toml
@@ -0,0 +1,8 @@
+[package]
+name = "foo"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_empty_members/out/crates/foo/src/main.rs b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_empty_members/out/crates/foo/src/main.rs
new file mode 100644
index 000000000..e7a11a969
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_empty_members/out/crates/foo/src/main.rs
@@ -0,0 +1,3 @@
+fn main() {
+ println!("Hello, world!");
+}
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_empty_members/stderr.log b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_empty_members/stderr.log
new file mode 100644
index 000000000..90150cdf5
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_empty_members/stderr.log
@@ -0,0 +1 @@
+ Created binary (application) `crates/foo` package
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_empty_members/stdout.log b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_empty_members/stdout.log
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_empty_members/stdout.log
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_exclude_list/in/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_exclude_list/in/Cargo.toml
new file mode 100644
index 000000000..4790076a8
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_exclude_list/in/Cargo.toml
@@ -0,0 +1,4 @@
+[workspace]
+resolver = "2"
+members = []
+exclude = ["crates/foo"]
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_exclude_list/in/src/lib.rs b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_exclude_list/in/src/lib.rs
new file mode 100644
index 000000000..7d12d9af8
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_exclude_list/in/src/lib.rs
@@ -0,0 +1,14 @@
+pub fn add(left: usize, right: usize) -> usize {
+ left + right
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn it_works() {
+ let result = add(2, 2);
+ assert_eq!(result, 4);
+ }
+}
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_exclude_list/mod.rs b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_exclude_list/mod.rs
new file mode 100644
index 000000000..9b9642468
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_exclude_list/mod.rs
@@ -0,0 +1,22 @@
+use cargo_test_support::compare::assert_ui;
+use cargo_test_support::curr_dir;
+use cargo_test_support::CargoCommand;
+use cargo_test_support::Project;
+
+#[cargo_test]
+fn case() {
+ let project = Project::from_template(curr_dir!().join("in"));
+ let project_root = project.root();
+ let cwd = &project_root;
+
+ snapbox::cmd::Command::cargo_ui()
+ .arg("new")
+ .args(["crates/foo"])
+ .current_dir(cwd)
+ .assert()
+ .success()
+ .stdout_matches_path(curr_dir!().join("stdout.log"))
+ .stderr_matches_path(curr_dir!().join("stderr.log"));
+
+ assert_ui().subset_matches(curr_dir!().join("out"), &project_root);
+}
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_exclude_list/out/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_exclude_list/out/Cargo.toml
new file mode 100644
index 000000000..4790076a8
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_exclude_list/out/Cargo.toml
@@ -0,0 +1,4 @@
+[workspace]
+resolver = "2"
+members = []
+exclude = ["crates/foo"]
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_exclude_list/out/crates/foo/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_exclude_list/out/crates/foo/Cargo.toml
new file mode 100644
index 000000000..1d9cfe317
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_exclude_list/out/crates/foo/Cargo.toml
@@ -0,0 +1,8 @@
+[package]
+name = "foo"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_exclude_list/out/crates/foo/src/main.rs b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_exclude_list/out/crates/foo/src/main.rs
new file mode 100644
index 000000000..e7a11a969
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_exclude_list/out/crates/foo/src/main.rs
@@ -0,0 +1,3 @@
+fn main() {
+ println!("Hello, world!");
+}
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_exclude_list/stderr.log b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_exclude_list/stderr.log
new file mode 100644
index 000000000..90150cdf5
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_exclude_list/stderr.log
@@ -0,0 +1 @@
+ Created binary (application) `crates/foo` package
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_exclude_list/stdout.log b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_exclude_list/stdout.log
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_exclude_list/stdout.log
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_members_glob/in/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_members_glob/in/Cargo.toml
new file mode 100644
index 000000000..a848b85b4
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_members_glob/in/Cargo.toml
@@ -0,0 +1,3 @@
+[workspace]
+resolver = "2"
+members = ["crates/*"]
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_members_glob/mod.rs b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_members_glob/mod.rs
new file mode 100644
index 000000000..9b9642468
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_members_glob/mod.rs
@@ -0,0 +1,22 @@
+use cargo_test_support::compare::assert_ui;
+use cargo_test_support::curr_dir;
+use cargo_test_support::CargoCommand;
+use cargo_test_support::Project;
+
+#[cargo_test]
+fn case() {
+ let project = Project::from_template(curr_dir!().join("in"));
+ let project_root = project.root();
+ let cwd = &project_root;
+
+ snapbox::cmd::Command::cargo_ui()
+ .arg("new")
+ .args(["crates/foo"])
+ .current_dir(cwd)
+ .assert()
+ .success()
+ .stdout_matches_path(curr_dir!().join("stdout.log"))
+ .stderr_matches_path(curr_dir!().join("stderr.log"));
+
+ assert_ui().subset_matches(curr_dir!().join("out"), &project_root);
+}
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_members_glob/out/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_members_glob/out/Cargo.toml
new file mode 100644
index 000000000..a848b85b4
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_members_glob/out/Cargo.toml
@@ -0,0 +1,3 @@
+[workspace]
+resolver = "2"
+members = ["crates/*"]
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_members_glob/out/crates/foo/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_members_glob/out/crates/foo/Cargo.toml
new file mode 100644
index 000000000..1d9cfe317
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_members_glob/out/crates/foo/Cargo.toml
@@ -0,0 +1,8 @@
+[package]
+name = "foo"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_members_glob/out/crates/foo/src/main.rs b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_members_glob/out/crates/foo/src/main.rs
new file mode 100644
index 000000000..e7a11a969
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_members_glob/out/crates/foo/src/main.rs
@@ -0,0 +1,3 @@
+fn main() {
+ println!("Hello, world!");
+}
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_members_glob/stderr.log b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_members_glob/stderr.log
new file mode 100644
index 000000000..90150cdf5
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_members_glob/stderr.log
@@ -0,0 +1 @@
+ Created binary (application) `crates/foo` package
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_members_glob/stdout.log b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_members_glob/stdout.log
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_members_glob/stdout.log
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/help/stdout.log b/src/tools/cargo/tests/testsuite/cargo_new/help/stdout.log
index a937f619b..3df5eceb8 100644
--- a/src/tools/cargo/tests/testsuite/cargo_new/help/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo_new/help/stdout.log
@@ -1,9 +1,9 @@
Create a new cargo package at <path>
-Usage: cargo[EXE] new [OPTIONS] <path>
+Usage: cargo[EXE] new [OPTIONS] <PATH>
Arguments:
- <path>
+ <PATH>
Options:
--vcs <VCS> Initialize a new repository for the given version control system,
@@ -12,7 +12,7 @@ Options:
--bin Use a binary (application) template [default]
--lib Use a library template
--edition <YEAR> Edition to set for the crate generated [possible values: 2015, 2018,
- 2021]
+ 2021, 2024]
--name <NAME> Set the resulting package name, defaults to the directory name
--registry <REGISTRY> Registry to use
-q, --quiet Do not print cargo log messages
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/mod.rs b/src/tools/cargo/tests/testsuite/cargo_new/mod.rs
index 969b09f4f..da0304409 100644
--- a/src/tools/cargo/tests/testsuite/cargo_new/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_new/mod.rs
@@ -1,3 +1,9 @@
+mod add_members_to_workspace_format_previous_items;
+mod add_members_to_workspace_format_sorted;
+mod add_members_to_workspace_with_absolute_package_path;
+mod add_members_to_workspace_with_empty_members;
+mod add_members_to_workspace_with_exclude_list;
+mod add_members_to_workspace_with_members_glob;
mod help;
mod inherit_workspace_lints;
mod inherit_workspace_package_table;
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/not_inherit_workspace_package_table_if_not_members/out/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_new/not_inherit_workspace_package_table_if_not_members/out/Cargo.toml
index 2d204581c..8de6ed4bf 100644
--- a/src/tools/cargo/tests/testsuite/cargo_new/not_inherit_workspace_package_table_if_not_members/out/Cargo.toml
+++ b/src/tools/cargo/tests/testsuite/cargo_new/not_inherit_workspace_package_table_if_not_members/out/Cargo.toml
@@ -1,3 +1,5 @@
+[workspace]
+members = ["foo"]
[workspace.package]
authors = ["Rustaceans"]
description = "foo"
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/not_inherit_workspace_package_table_if_not_members/stderr.log b/src/tools/cargo/tests/testsuite/cargo_new/not_inherit_workspace_package_table_if_not_members/stderr.log
index 03b1ff6db..9b5015924 100644
--- a/src/tools/cargo/tests/testsuite/cargo_new/not_inherit_workspace_package_table_if_not_members/stderr.log
+++ b/src/tools/cargo/tests/testsuite/cargo_new/not_inherit_workspace_package_table_if_not_members/stderr.log
@@ -1,9 +1 @@
-warning: compiling this new package may not work due to invalid workspace configuration
-
-current package believes it's in a workspace when it's not:
-current: [ROOT]/case/foo/Cargo.toml
-workspace: [ROOT]/case/Cargo.toml
-
-this may be fixable by adding `foo` to the `workspace.members` array of the manifest located at: [ROOT]/case/Cargo.toml
-Alternatively, to keep it out of the workspace, add the package to the `workspace.exclude` array, or add an empty `[workspace]` table to the package's manifest.
Created binary (application) `foo` package
diff --git a/src/tools/cargo/tests/testsuite/cargo_owner/help/stdout.log b/src/tools/cargo/tests/testsuite/cargo_owner/help/stdout.log
index 580be3c88..110df8e9a 100644
--- a/src/tools/cargo/tests/testsuite/cargo_owner/help/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo_owner/help/stdout.log
@@ -1,9 +1,9 @@
Manage the owners of a crate on the registry
-Usage: cargo[EXE] owner [OPTIONS] [crate]
+Usage: cargo[EXE] owner [OPTIONS] [CRATE]
Arguments:
- [crate]
+ [CRATE]
Options:
-a, --add <LOGIN> Name of a user or team to invite as an owner
diff --git a/src/tools/cargo/tests/testsuite/cargo_pkgid/help/stdout.log b/src/tools/cargo/tests/testsuite/cargo_pkgid/help/stdout.log
index ed48bb7ea..5971e88dc 100644
--- a/src/tools/cargo/tests/testsuite/cargo_pkgid/help/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo_pkgid/help/stdout.log
@@ -1,9 +1,9 @@
Print a fully qualified package specification
-Usage: cargo[EXE] pkgid [OPTIONS] [spec]
+Usage: cargo[EXE] pkgid [OPTIONS] [SPEC]
Arguments:
- [spec]
+ [SPEC]
Options:
-q, --quiet Do not print cargo log messages
diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/help/stdout.log b/src/tools/cargo/tests/testsuite/cargo_remove/help/stdout.log
index 8937fb9f3..47d2c87ad 100644
--- a/src/tools/cargo/tests/testsuite/cargo_remove/help/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo_remove/help/stdout.log
@@ -15,9 +15,9 @@ Options:
-h, --help Print help
Section:
- --dev Remove as development dependency
- --build Remove as build dependency
- --target <TARGET> Remove as dependency from the given target platform
+ --dev Remove from dev-dependencies
+ --build Remove from build-dependencies
+ --target <TARGET> Remove from target-dependencies
Package Selection:
-p, --package [<SPEC>] Package to remove from
diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/invalid_dep/stderr.log b/src/tools/cargo/tests/testsuite/cargo_remove/invalid_dep/stderr.log
index eea124d65..9bb137d1f 100644
--- a/src/tools/cargo/tests/testsuite/cargo_remove/invalid_dep/stderr.log
+++ b/src/tools/cargo/tests/testsuite/cargo_remove/invalid_dep/stderr.log
@@ -1,2 +1,2 @@
Removing invalid_dependency_name from dependencies
-error: the dependency `invalid_dependency_name` could not be found in `dependencies`.
+error: the dependency `invalid_dependency_name` could not be found in `dependencies`
diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/invalid_section/stderr.log b/src/tools/cargo/tests/testsuite/cargo_remove/invalid_section/stderr.log
index fff5ff00a..8cbafa98e 100644
--- a/src/tools/cargo/tests/testsuite/cargo_remove/invalid_section/stderr.log
+++ b/src/tools/cargo/tests/testsuite/cargo_remove/invalid_section/stderr.log
@@ -1,2 +1,2 @@
Removing docopt from build-dependencies
-error: the dependency `docopt` could not be found in `build-dependencies`.
+error: the dependency `docopt` could not be found in `build-dependencies`; it is present in `dependencies`
diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/invalid_section_dep/stderr.log b/src/tools/cargo/tests/testsuite/cargo_remove/invalid_section_dep/stderr.log
index 1926f9577..60c0f8b41 100644
--- a/src/tools/cargo/tests/testsuite/cargo_remove/invalid_section_dep/stderr.log
+++ b/src/tools/cargo/tests/testsuite/cargo_remove/invalid_section_dep/stderr.log
@@ -1,2 +1,2 @@
Removing semver from dev-dependencies
-error: the dependency `semver` could not be found in `dev-dependencies`.
+error: the dependency `semver` could not be found in `dev-dependencies`; it is present in `dependencies`
diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/invalid_target/stderr.log b/src/tools/cargo/tests/testsuite/cargo_remove/invalid_target/stderr.log
index 5075b80b7..eae9f7887 100644
--- a/src/tools/cargo/tests/testsuite/cargo_remove/invalid_target/stderr.log
+++ b/src/tools/cargo/tests/testsuite/cargo_remove/invalid_target/stderr.log
@@ -1,2 +1,2 @@
Removing dbus from dependencies for target `powerpc-unknown-linux-gnu`
-error: the dependency `dbus` could not be found in `target.powerpc-unknown-linux-gnu.dependencies`.
+error: the dependency `dbus` could not be found in `target.powerpc-unknown-linux-gnu.dependencies`; it is present in `target.x86_64-unknown-linux-gnu.dependencies`
diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/invalid_target_dep/stderr.log b/src/tools/cargo/tests/testsuite/cargo_remove/invalid_target_dep/stderr.log
index 54bfe085f..635a7bd6c 100644
--- a/src/tools/cargo/tests/testsuite/cargo_remove/invalid_target_dep/stderr.log
+++ b/src/tools/cargo/tests/testsuite/cargo_remove/invalid_target_dep/stderr.log
@@ -1,2 +1,2 @@
Removing toml from dependencies for target `x86_64-unknown-linux-gnu`
-error: the dependency `toml` could not be found in `target.x86_64-unknown-linux-gnu.dependencies`.
+error: the dependency `toml` could not be found in `target.x86_64-unknown-linux-gnu.dependencies`; it is present in `dependencies`
diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/mod.rs b/src/tools/cargo/tests/testsuite/cargo_remove/mod.rs
index 4403e2425..7b9190642 100644
--- a/src/tools/cargo/tests/testsuite/cargo_remove/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_remove/mod.rs
@@ -20,6 +20,7 @@ mod multiple_dev;
mod no_arg;
mod offline;
mod optional_dep_feature;
+mod optional_dep_feature_formatting;
mod optional_feature;
mod package;
mod remove_basic;
diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/multiple_dev/out/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_remove/multiple_dev/out/Cargo.toml
index d961b2bb1..b435e33eb 100644
--- a/src/tools/cargo/tests/testsuite/cargo_remove/multiple_dev/out/Cargo.toml
+++ b/src/tools/cargo/tests/testsuite/cargo_remove/multiple_dev/out/Cargo.toml
@@ -17,4 +17,4 @@ toml = "0.1"
clippy = "0.4"
[features]
-std = ["semver/std"]
+std = [ "semver/std"]
diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/optional_dep_feature/out/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_remove/optional_dep_feature/out/Cargo.toml
index 63112d334..f9613bd30 100644
--- a/src/tools/cargo/tests/testsuite/cargo_remove/optional_dep_feature/out/Cargo.toml
+++ b/src/tools/cargo/tests/testsuite/cargo_remove/optional_dep_feature/out/Cargo.toml
@@ -20,4 +20,4 @@ clippy = "0.4"
regex = "0.1.1"
[features]
-std = ["semver/std"]
+std = [ "semver/std"]
diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/optional_dep_feature_formatting/in/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_remove/optional_dep_feature_formatting/in/Cargo.toml
new file mode 100644
index 000000000..01755d687
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_remove/optional_dep_feature_formatting/in/Cargo.toml
@@ -0,0 +1,42 @@
+[package]
+name = "cargo-remove-test-fixture"
+version = "0.1.0"
+
+[[bin]]
+name = "main"
+path = "src/main.rs"
+
+[build-dependencies]
+semver = "0.1.0"
+
+[dependencies]
+docopt = { version = "0.6", optional = true }
+rustc-serialize = { version = "0.4", optional = true }
+semver = "0.1"
+toml = { version = "0.1", optional = true }
+clippy = { version = "0.4", optional = true }
+
+[dev-dependencies]
+regex = "0.1.1"
+serde = "1.0.90"
+
+[features]
+std = [
+ # Leading clippy
+ "dep:clippy", # trailing clippy
+
+ # Leading docopt
+ "dep:docopt", # trailing docopt
+
+ # Leading rustc-serialize
+ "dep:rustc-serialize", # trailing rustc-serialize
+
+ # Leading serde/std
+ "serde/std", # trailing serde/std
+
+ # Leading semver/std
+ "semver/std", # trailing semver/std
+
+ # Leading toml
+ "dep:toml", # trailing toml
+]
diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/optional_dep_feature_formatting/in/src/lib.rs b/src/tools/cargo/tests/testsuite/cargo_remove/optional_dep_feature_formatting/in/src/lib.rs
new file mode 100644
index 000000000..8b1378917
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_remove/optional_dep_feature_formatting/in/src/lib.rs
@@ -0,0 +1 @@
+
diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/optional_dep_feature_formatting/mod.rs b/src/tools/cargo/tests/testsuite/cargo_remove/optional_dep_feature_formatting/mod.rs
new file mode 100644
index 000000000..69406387b
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_remove/optional_dep_feature_formatting/mod.rs
@@ -0,0 +1,35 @@
+use cargo_test_support::compare::assert_ui;
+use cargo_test_support::curr_dir;
+use cargo_test_support::CargoCommand;
+use cargo_test_support::Project;
+
+#[cargo_test]
+fn case() {
+ cargo_test_support::registry::init();
+ cargo_test_support::registry::Package::new("clippy", "0.4.0+my-package").publish();
+ cargo_test_support::registry::Package::new("docopt", "0.6.2+my-package").publish();
+ cargo_test_support::registry::Package::new("regex", "0.1.1+my-package").publish();
+ cargo_test_support::registry::Package::new("rustc-serialize", "0.4.0+my-package").publish();
+ cargo_test_support::registry::Package::new("toml", "0.1.1+my-package").publish();
+ cargo_test_support::registry::Package::new("semver", "0.1.1")
+ .feature("std", &[])
+ .publish();
+ cargo_test_support::registry::Package::new("serde", "1.0.90")
+ .feature("std", &[])
+ .publish();
+
+ let project = Project::from_template(curr_dir!().join("in"));
+ let project_root = project.root();
+ let cwd = &project_root;
+
+ snapbox::cmd::Command::cargo_ui()
+ .arg("remove")
+ .args(["docopt", "toml"])
+ .current_dir(cwd)
+ .assert()
+ .success()
+ .stdout_matches_path(curr_dir!().join("stdout.log"))
+ .stderr_matches_path(curr_dir!().join("stderr.log"));
+
+ assert_ui().subset_matches(curr_dir!().join("out"), &project_root);
+}
diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/optional_dep_feature_formatting/out/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_remove/optional_dep_feature_formatting/out/Cargo.toml
new file mode 100644
index 000000000..0398c8beb
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_remove/optional_dep_feature_formatting/out/Cargo.toml
@@ -0,0 +1,40 @@
+[package]
+name = "cargo-remove-test-fixture"
+version = "0.1.0"
+
+[[bin]]
+name = "main"
+path = "src/main.rs"
+
+[build-dependencies]
+semver = "0.1.0"
+
+[dependencies]
+rustc-serialize = { version = "0.4", optional = true }
+semver = "0.1"
+clippy = { version = "0.4", optional = true }
+
+[dev-dependencies]
+regex = "0.1.1"
+serde = "1.0.90"
+
+[features]
+std = [
+ # Leading clippy
+ "dep:clippy", # trailing clippy
+
+ # Leading docopt
+ # trailing docopt
+
+ # Leading rustc-serialize
+ "dep:rustc-serialize", # trailing rustc-serialize
+
+ # Leading serde/std
+ "serde/std", # trailing serde/std
+
+ # Leading semver/std
+ "semver/std", # trailing semver/std
+
+ # Leading toml
+ # trailing toml
+]
diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/optional_dep_feature_formatting/stderr.log b/src/tools/cargo/tests/testsuite/cargo_remove/optional_dep_feature_formatting/stderr.log
new file mode 100644
index 000000000..7bceb0f94
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_remove/optional_dep_feature_formatting/stderr.log
@@ -0,0 +1,2 @@
+ Removing docopt from dependencies
+ Removing toml from dependencies
diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/optional_dep_feature_formatting/stdout.log b/src/tools/cargo/tests/testsuite/cargo_remove/optional_dep_feature_formatting/stdout.log
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_remove/optional_dep_feature_formatting/stdout.log
diff --git a/src/tools/cargo/tests/testsuite/cargo_run/help/stdout.log b/src/tools/cargo/tests/testsuite/cargo_run/help/stdout.log
index 4b39f30b3..97c13382a 100644
--- a/src/tools/cargo/tests/testsuite/cargo_run/help/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo_run/help/stdout.log
@@ -1,9 +1,9 @@
Run a binary or example of the local package
-Usage: cargo[EXE] run [OPTIONS] [args]...
+Usage: cargo[EXE] run [OPTIONS] [ARGS]...
Arguments:
- [args]... Arguments for the binary or example to run
+ [ARGS]... Arguments for the binary or example to run
Options:
--ignore-rust-version Ignore `rust-version` specification in packages
diff --git a/src/tools/cargo/tests/testsuite/cargo_rustc/help/stdout.log b/src/tools/cargo/tests/testsuite/cargo_rustc/help/stdout.log
index 9d43841fe..60069f526 100644
--- a/src/tools/cargo/tests/testsuite/cargo_rustc/help/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo_rustc/help/stdout.log
@@ -1,9 +1,9 @@
Compile a package, and pass extra options to the compiler
-Usage: cargo[EXE] rustc [OPTIONS] [args]...
+Usage: cargo[EXE] rustc [OPTIONS] [ARGS]...
Arguments:
- [args]... Extra rustc flags
+ [ARGS]... Extra rustc flags
Options:
--print <INFO> Output compiler information without compiling
@@ -28,9 +28,9 @@ Target Selection:
--bin [<NAME>] Build only the specified binary
--examples Build all examples
--example [<NAME>] Build only the specified example
- --tests Build all tests
+ --tests Build all test targets
--test [<NAME>] Build only the specified test target
- --benches Build all benches
+ --benches Build all bench targets
--bench [<NAME>] Build only the specified bench target
--all-targets Build all targets
diff --git a/src/tools/cargo/tests/testsuite/cargo_rustdoc/help/stdout.log b/src/tools/cargo/tests/testsuite/cargo_rustdoc/help/stdout.log
index 706072f24..67ee27e6b 100644
--- a/src/tools/cargo/tests/testsuite/cargo_rustdoc/help/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo_rustdoc/help/stdout.log
@@ -1,9 +1,9 @@
Build a package's documentation, using specified custom flags.
-Usage: cargo[EXE] rustdoc [OPTIONS] [args]...
+Usage: cargo[EXE] rustdoc [OPTIONS] [ARGS]...
Arguments:
- [args]... Extra rustdoc flags
+ [ARGS]... Extra rustdoc flags
Options:
--open Opens the docs in a browser after the operation
@@ -26,9 +26,9 @@ Target Selection:
--bin [<NAME>] Build only the specified binary
--examples Build all examples
--example [<NAME>] Build only the specified example
- --tests Build all tests
+ --tests Build all test targets
--test [<NAME>] Build only the specified test target
- --benches Build all benches
+ --benches Build all bench targets
--bench [<NAME>] Build only the specified bench target
--all-targets Build all targets
diff --git a/src/tools/cargo/tests/testsuite/cargo_search/help/stdout.log b/src/tools/cargo/tests/testsuite/cargo_search/help/stdout.log
index 07170ad70..9cc508bba 100644
--- a/src/tools/cargo/tests/testsuite/cargo_search/help/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo_search/help/stdout.log
@@ -1,9 +1,9 @@
Search packages in crates.io
-Usage: cargo[EXE] search [OPTIONS] [query]...
+Usage: cargo[EXE] search [OPTIONS] [QUERY]...
Arguments:
- [query]...
+ [QUERY]...
Options:
--limit <LIMIT> Limit the number of results (default: 10, max: 100)
diff --git a/src/tools/cargo/tests/testsuite/cargo_test/help/stdout.log b/src/tools/cargo/tests/testsuite/cargo_test/help/stdout.log
index 5df62d6bb..d7ec18f46 100644
--- a/src/tools/cargo/tests/testsuite/cargo_test/help/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo_test/help/stdout.log
@@ -1,10 +1,10 @@
Execute all unit and integration tests and build examples of a local package
-Usage: cargo[EXE] test [OPTIONS] [TESTNAME] [-- [args]...]
+Usage: cargo[EXE] test [OPTIONS] [TESTNAME] [-- [ARGS]...]
Arguments:
[TESTNAME] If specified, only run tests containing this string in their names
- [args]... Arguments for the test binary
+ [ARGS]... Arguments for the test binary
Options:
--doc Test only this library's documentation
@@ -33,9 +33,9 @@ Target Selection:
--bin [<NAME>] Test only the specified binary
--examples Test all examples
--example [<NAME>] Test only the specified example
- --tests Test all tests
+ --tests Test all test targets
--test [<NAME>] Test only the specified test target
- --benches Test all benches
+ --benches Test all bench targets
--bench [<NAME>] Test only the specified bench target
--all-targets Test all targets (does not include doctests)
diff --git a/src/tools/cargo/tests/testsuite/cargo_test/no_keep_going/stderr.log b/src/tools/cargo/tests/testsuite/cargo_test/no_keep_going/stderr.log
index fd4ca9b2a..22323e651 100644
--- a/src/tools/cargo/tests/testsuite/cargo_test/no_keep_going/stderr.log
+++ b/src/tools/cargo/tests/testsuite/cargo_test/no_keep_going/stderr.log
@@ -2,6 +2,6 @@ error: unexpected argument '--keep-going' found
tip: use `--no-fail-fast` to run as many tests as possible regardless of failure
-Usage: cargo[EXE] test [OPTIONS] [TESTNAME] [-- [args]...]
+Usage: cargo[EXE] test [OPTIONS] [TESTNAME] [-- [ARGS]...]
For more information, try '--help'.
diff --git a/src/tools/cargo/tests/testsuite/cargo_uninstall/help/stdout.log b/src/tools/cargo/tests/testsuite/cargo_uninstall/help/stdout.log
index 2da1a5d57..efdf11c03 100644
--- a/src/tools/cargo/tests/testsuite/cargo_uninstall/help/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo_uninstall/help/stdout.log
@@ -1,9 +1,9 @@
Remove a Rust binary
-Usage: cargo[EXE] uninstall [OPTIONS] [spec]...
+Usage: cargo[EXE] uninstall [OPTIONS] [SPEC]...
Arguments:
- [spec]...
+ [SPEC]...
Options:
--root <DIR> Directory to uninstall packages from
diff --git a/src/tools/cargo/tests/testsuite/cargo_yank/help/stdout.log b/src/tools/cargo/tests/testsuite/cargo_yank/help/stdout.log
index c6dbfeb9d..61dc800c7 100644
--- a/src/tools/cargo/tests/testsuite/cargo_yank/help/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo_yank/help/stdout.log
@@ -1,9 +1,9 @@
Remove a pushed crate from the index
-Usage: cargo[EXE] yank [OPTIONS] [crate]
+Usage: cargo[EXE] yank [OPTIONS] [CRATE]
Arguments:
- [crate]
+ [CRATE]
Options:
--version <VERSION> The version to yank or un-yank
diff --git a/src/tools/cargo/tests/testsuite/check.rs b/src/tools/cargo/tests/testsuite/check.rs
index b74bd6209..03611ae67 100644
--- a/src/tools/cargo/tests/testsuite/check.rs
+++ b/src/tools/cargo/tests/testsuite/check.rs
@@ -1496,3 +1496,26 @@ fn check_unused_manifest_keys() {
)
.run();
}
+
+#[cargo_test]
+fn versionless_package() {
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ description = "foo"
+ "#,
+ )
+ .file("src/lib.rs", "")
+ .build();
+ p.cargo("check")
+ .with_stderr(
+ "\
+[CHECKING] foo v0.0.0 ([CWD])
+[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+",
+ )
+ .run();
+}
diff --git a/src/tools/cargo/tests/testsuite/check_cfg.rs b/src/tools/cargo/tests/testsuite/check_cfg.rs
index c35da637d..57d5f8053 100644
--- a/src/tools/cargo/tests/testsuite/check_cfg.rs
+++ b/src/tools/cargo/tests/testsuite/check_cfg.rs
@@ -15,16 +15,16 @@ macro_rules! x {
$what, '(', $($who,)* ')', "'", "[..]")
}
}};
- ($tool:tt => $what:tt of $who:tt with $($values:tt)*) => {{
+ ($tool:tt => $what:tt of $who:tt with $($first_value:tt $($other_values:tt)*)?) => {{
#[cfg(windows)]
{
concat!("[RUNNING] [..]", $tool, "[..] --check-cfg \"",
- $what, '(', $who, $(", ", "/\"", $values, "/\"",)* ")", '"', "[..]")
+ $what, '(', $who, ", values(", $("/\"", $first_value, "/\"", $(", ", "/\"", $other_values, "/\"",)*)* "))", '"', "[..]")
}
#[cfg(not(windows))]
{
concat!("[RUNNING] [..]", $tool, "[..] --check-cfg '",
- $what, '(', $who, $(", ", "\"", $values, "\"",)* ")", "'", "[..]")
+ $what, '(', $who, ", values(", $("\"", $first_value, "\"", $(", ", "\"", $other_values, "\"",)*)* "))", "'", "[..]")
}
}};
}
@@ -47,9 +47,9 @@ fn features() {
.file("src/main.rs", "fn main() {}")
.build();
- p.cargo("check -v -Zcheck-cfg=features")
+ p.cargo("check -v -Zcheck-cfg")
.masquerade_as_nightly_cargo(&["check-cfg"])
- .with_stderr_contains(x!("rustc" => "values" of "feature" with "f_a" "f_b"))
+ .with_stderr_contains(x!("rustc" => "cfg" of "feature" with "f_a" "f_b"))
.run();
}
@@ -76,10 +76,9 @@ fn features_with_deps() {
.file("bar/src/lib.rs", "#[allow(dead_code)] fn bar() {}")
.build();
- p.cargo("check -v -Zcheck-cfg=features")
+ p.cargo("check -v -Zcheck-cfg")
.masquerade_as_nightly_cargo(&["check-cfg"])
- .with_stderr_contains(x!("rustc" => "values" of "feature"))
- .with_stderr_contains(x!("rustc" => "values" of "feature" with "f_a" "f_b"))
+ .with_stderr_contains(x!("rustc" => "cfg" of "feature" with "f_a" "f_b"))
.run();
}
@@ -107,10 +106,9 @@ fn features_with_opt_deps() {
.file("bar/src/lib.rs", "#[allow(dead_code)] fn bar() {}")
.build();
- p.cargo("check -v -Zcheck-cfg=features")
+ p.cargo("check -v -Zcheck-cfg")
.masquerade_as_nightly_cargo(&["check-cfg"])
- .with_stderr_contains(x!("rustc" => "values" of "feature"))
- .with_stderr_contains(x!("rustc" => "values" of "feature" with "bar" "default" "f_a" "f_b"))
+ .with_stderr_contains(x!("rustc" => "cfg" of "feature" with "bar" "default" "f_a" "f_b"))
.run();
}
@@ -137,111 +135,22 @@ fn features_with_namespaced_features() {
.file("bar/src/lib.rs", "#[allow(dead_code)] fn bar() {}")
.build();
- p.cargo("check -v -Zcheck-cfg=features")
+ p.cargo("check -v -Zcheck-cfg")
.masquerade_as_nightly_cargo(&["check-cfg"])
- .with_stderr_contains(x!("rustc" => "values" of "feature" with "f_a" "f_b"))
+ .with_stderr_contains(x!("rustc" => "cfg" of "feature" with "f_a" "f_b"))
.run();
}
#[cargo_test(nightly, reason = "--check-cfg is unstable")]
-fn well_known_names() {
+fn well_known_names_values() {
let p = project()
.file("Cargo.toml", &basic_manifest("foo", "0.1.0"))
.file("src/main.rs", "fn main() {}")
.build();
- p.cargo("check -v -Zcheck-cfg=names")
+ p.cargo("check -v -Zcheck-cfg")
.masquerade_as_nightly_cargo(&["check-cfg"])
- .with_stderr_contains(x!("rustc" => "names"))
- .run();
-}
-
-#[cargo_test(nightly, reason = "--check-cfg is unstable")]
-fn well_known_values() {
- let p = project()
- .file("Cargo.toml", &basic_manifest("foo", "0.1.0"))
- .file("src/main.rs", "fn main() {}")
- .build();
-
- p.cargo("check -v -Zcheck-cfg=values")
- .masquerade_as_nightly_cargo(&["check-cfg"])
- .with_stderr_contains(x!("rustc" => "values"))
- .run();
-}
-
-#[cargo_test(nightly, reason = "--check-cfg is unstable")]
-fn cli_all_options() {
- let p = project()
- .file(
- "Cargo.toml",
- r#"
- [package]
- name = "foo"
- version = "0.1.0"
-
- [features]
- f_a = []
- f_b = []
- "#,
- )
- .file("src/main.rs", "fn main() {}")
- .build();
-
- p.cargo("check -v -Zcheck-cfg=features,names,values")
- .masquerade_as_nightly_cargo(&["check-cfg"])
- .with_stderr_contains(x!("rustc" => "names"))
- .with_stderr_contains(x!("rustc" => "values"))
- .with_stderr_contains(x!("rustc" => "values" of "feature" with "f_a" "f_b"))
- .run();
-}
-
-#[cargo_test(nightly, reason = "--check-cfg is unstable")]
-fn features_with_cargo_check() {
- let p = project()
- .file(
- "Cargo.toml",
- r#"
- [package]
- name = "foo"
- version = "0.1.0"
-
- [features]
- f_a = []
- f_b = []
- "#,
- )
- .file("src/main.rs", "fn main() {}")
- .build();
-
- p.cargo("check -v -Zcheck-cfg=features")
- .masquerade_as_nightly_cargo(&["check-cfg"])
- .with_stderr_contains(x!("rustc" => "values" of "feature" with "f_a" "f_b"))
- .run();
-}
-
-#[cargo_test(nightly, reason = "--check-cfg is unstable")]
-fn well_known_names_with_check() {
- let p = project()
- .file("Cargo.toml", &basic_manifest("foo", "0.1.0"))
- .file("src/main.rs", "fn main() {}")
- .build();
-
- p.cargo("check -v -Zcheck-cfg=names")
- .masquerade_as_nightly_cargo(&["check-cfg"])
- .with_stderr_contains(x!("rustc" => "names"))
- .run();
-}
-
-#[cargo_test(nightly, reason = "--check-cfg is unstable")]
-fn well_known_values_with_check() {
- let p = project()
- .file("Cargo.toml", &basic_manifest("foo", "0.1.0"))
- .file("src/main.rs", "fn main() {}")
- .build();
-
- p.cargo("check -v -Zcheck-cfg=values")
- .masquerade_as_nightly_cargo(&["check-cfg"])
- .with_stderr_contains(x!("rustc" => "values"))
+ .with_stderr_contains(x!("rustc" => "cfg" of "feature" with))
.run();
}
@@ -263,9 +172,9 @@ fn features_test() {
.file("src/main.rs", "fn main() {}")
.build();
- p.cargo("test -v -Zcheck-cfg=features")
+ p.cargo("test -v -Zcheck-cfg")
.masquerade_as_nightly_cargo(&["check-cfg"])
- .with_stderr_contains(x!("rustc" => "values" of "feature" with "f_a" "f_b"))
+ .with_stderr_contains(x!("rustc" => "cfg" of "feature" with "f_a" "f_b"))
.run();
}
@@ -288,64 +197,37 @@ fn features_doctest() {
.file("src/lib.rs", "#[allow(dead_code)] fn foo() {}")
.build();
- p.cargo("test -v --doc -Zcheck-cfg=features")
+ p.cargo("test -v --doc -Zcheck-cfg")
.masquerade_as_nightly_cargo(&["check-cfg"])
- .with_stderr_contains(x!("rustc" => "values" of "feature" with "default" "f_a" "f_b"))
- .with_stderr_contains(x!("rustdoc" => "values" of "feature" with "default" "f_a" "f_b"))
+ .with_stderr_contains(x!("rustc" => "cfg" of "feature" with "default" "f_a" "f_b"))
+ .with_stderr_contains(x!("rustdoc" => "cfg" of "feature" with "default" "f_a" "f_b"))
.run();
}
#[cargo_test(nightly, reason = "--check-cfg is unstable")]
-fn well_known_names_test() {
+fn well_known_names_values_test() {
let p = project()
.file("Cargo.toml", &basic_manifest("foo", "0.1.0"))
.file("src/main.rs", "fn main() {}")
.build();
- p.cargo("test -v -Zcheck-cfg=names")
+ p.cargo("test -v -Zcheck-cfg")
.masquerade_as_nightly_cargo(&["check-cfg"])
- .with_stderr_contains(x!("rustc" => "names"))
+ .with_stderr_contains(x!("rustc" => "cfg" of "feature" with))
.run();
}
#[cargo_test(nightly, reason = "--check-cfg is unstable")]
-fn well_known_values_test() {
- let p = project()
- .file("Cargo.toml", &basic_manifest("foo", "0.1.0"))
- .file("src/main.rs", "fn main() {}")
- .build();
-
- p.cargo("test -v -Zcheck-cfg=values")
- .masquerade_as_nightly_cargo(&["check-cfg"])
- .with_stderr_contains(x!("rustc" => "values"))
- .run();
-}
-
-#[cargo_test(nightly, reason = "--check-cfg is unstable")]
-fn well_known_names_doctest() {
+fn well_known_names_values_doctest() {
let p = project()
.file("Cargo.toml", &basic_manifest("foo", "0.1.0"))
.file("src/lib.rs", "#[allow(dead_code)] fn foo() {}")
.build();
- p.cargo("test -v --doc -Zcheck-cfg=names")
+ p.cargo("test -v --doc -Zcheck-cfg")
.masquerade_as_nightly_cargo(&["check-cfg"])
- .with_stderr_contains(x!("rustc" => "names"))
- .with_stderr_contains(x!("rustdoc" => "names"))
- .run();
-}
-
-#[cargo_test(nightly, reason = "--check-cfg is unstable")]
-fn well_known_values_doctest() {
- let p = project()
- .file("Cargo.toml", &basic_manifest("foo", "0.1.0"))
- .file("src/lib.rs", "#[allow(dead_code)] fn foo() {}")
- .build();
-
- p.cargo("test -v --doc -Zcheck-cfg=values")
- .masquerade_as_nightly_cargo(&["check-cfg"])
- .with_stderr_contains(x!("rustc" => "values"))
- .with_stderr_contains(x!("rustdoc" => "values"))
+ .with_stderr_contains(x!("rustc" => "cfg" of "feature" with))
+ .with_stderr_contains(x!("rustdoc" => "cfg" of "feature" with))
.run();
}
@@ -368,9 +250,9 @@ fn features_doc() {
.file("src/lib.rs", "#[allow(dead_code)] fn foo() {}")
.build();
- p.cargo("doc -v -Zcheck-cfg=features")
+ p.cargo("doc -v -Zcheck-cfg")
.masquerade_as_nightly_cargo(&["check-cfg"])
- .with_stderr_contains(x!("rustdoc" => "values" of "feature" with "default" "f_a" "f_b"))
+ .with_stderr_contains(x!("rustdoc" => "cfg" of "feature" with "default" "f_a" "f_b"))
.run();
}
@@ -390,13 +272,13 @@ fn build_script_feedback() {
.file("src/main.rs", "fn main() {}")
.file(
"build.rs",
- r#"fn main() { println!("cargo:rustc-check-cfg=names(foo)"); }"#,
+ r#"fn main() { println!("cargo:rustc-check-cfg=cfg(foo)"); }"#,
)
.build();
- p.cargo("check -v -Zcheck-cfg=output")
+ p.cargo("check -v -Zcheck-cfg")
.masquerade_as_nightly_cargo(&["check-cfg"])
- .with_stderr_contains(x!("rustc" => "names" of "foo"))
+ .with_stderr_contains(x!("rustc" => "cfg" of "foo"))
.run();
}
@@ -416,12 +298,13 @@ fn build_script_doc() {
.file("src/main.rs", "fn main() {}")
.file(
"build.rs",
- r#"fn main() { println!("cargo:rustc-check-cfg=names(foo)"); }"#,
+ r#"fn main() { println!("cargo:rustc-check-cfg=cfg(foo)"); }"#,
)
.build();
- p.cargo("doc -v -Zcheck-cfg=output")
+
+ p.cargo("doc -v -Zcheck-cfg")
.with_stderr_does_not_contain("rustc [..] --check-cfg [..]")
- .with_stderr_contains(x!("rustdoc" => "names" of "foo"))
+ .with_stderr_contains(x!("rustdoc" => "cfg" of "foo"))
.with_stderr(
"\
[COMPILING] foo v0.0.1 ([CWD])
@@ -429,7 +312,9 @@ fn build_script_doc() {
[RUNNING] `[..]/build-script-build`
[DOCUMENTING] foo [..]
[RUNNING] `rustdoc [..] src/main.rs [..]
-[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]",
+[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+[GENERATED] [CWD]/target/doc/foo/index.html
+",
)
.masquerade_as_nightly_cargo(&["check-cfg"])
.run();
@@ -458,19 +343,54 @@ fn build_script_override() {
&format!(
r#"
[target.{}.a]
- rustc-check-cfg = ["names(foo)"]
+ rustc-check-cfg = ["cfg(foo)"]
"#,
target
),
)
.build();
- p.cargo("check -v -Zcheck-cfg=output")
- .with_stderr_contains(x!("rustc" => "names" of "foo"))
+ p.cargo("check -v -Zcheck-cfg")
+ .with_stderr_contains(x!("rustc" => "cfg" of "foo"))
.masquerade_as_nightly_cargo(&["check-cfg"])
.run();
}
+#[cargo_test]
+fn build_script_override_feature_gate() {
+ let target = cargo_test_support::rustc_host();
+
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.1.0"
+ links = "a"
+ "#,
+ )
+ .file("src/main.rs", "fn main() {}")
+ .file("build.rs", "fn main() {}")
+ .file(
+ ".cargo/config",
+ &format!(
+ r#"
+ [target.{}.a]
+ rustc-check-cfg = ["cfg(foo)"]
+ "#,
+ target
+ ),
+ )
+ .build();
+
+ p.cargo("check")
+ .with_stderr_contains(
+ "warning: target config[..]rustc-check-cfg[..] requires -Zcheck-cfg flag",
+ )
+ .run();
+}
+
#[cargo_test(nightly, reason = "--check-cfg is unstable")]
fn build_script_test() {
let p = project()
@@ -487,7 +407,7 @@ fn build_script_test() {
.file(
"build.rs",
r#"fn main() {
- println!("cargo:rustc-check-cfg=names(foo)");
+ println!("cargo:rustc-check-cfg=cfg(foo)");
println!("cargo:rustc-cfg=foo");
}"#,
)
@@ -516,9 +436,9 @@ fn build_script_test() {
.file("tests/test.rs", "#[cfg(foo)] #[test] fn test_bar() {}")
.build();
- p.cargo("test -v -Zcheck-cfg=output")
- .with_stderr_contains(x!("rustc" => "names" of "foo"))
- .with_stderr_contains(x!("rustdoc" => "names" of "foo"))
+ p.cargo("test -v -Zcheck-cfg")
+ .with_stderr_contains(x!("rustc" => "cfg" of "foo"))
+ .with_stderr_contains(x!("rustdoc" => "cfg" of "foo"))
.with_stdout_contains("test test_foo ... ok")
.with_stdout_contains("test test_bar ... ok")
.with_stdout_contains_n("test [..] ... ok", 3)
@@ -526,6 +446,34 @@ fn build_script_test() {
.run();
}
+#[cargo_test]
+fn build_script_feature_gate() {
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+ build = "build.rs"
+ "#,
+ )
+ .file(
+ "build.rs",
+ r#"fn main() {
+ println!("cargo:rustc-check-cfg=cfg(foo)");
+ println!("cargo:rustc-cfg=foo");
+ }"#,
+ )
+ .file("src/main.rs", "fn main() {}")
+ .build();
+
+ p.cargo("check")
+ .with_stderr_contains("warning[..]cargo:rustc-check-cfg requires -Zcheck-cfg flag")
+ .with_status(0)
+ .run();
+}
+
#[cargo_test(nightly, reason = "--check-cfg is unstable")]
fn config_valid() {
let p = project()
@@ -546,16 +494,14 @@ fn config_valid() {
".cargo/config.toml",
r#"
[unstable]
- check-cfg = ["features", "names", "values"]
+ check-cfg = true
"#,
)
.build();
- p.cargo("check -v -Zcheck-cfg=features,names,values")
+ p.cargo("check -v")
.masquerade_as_nightly_cargo(&["check-cfg"])
- .with_stderr_contains(x!("rustc" => "names"))
- .with_stderr_contains(x!("rustc" => "values"))
- .with_stderr_contains(x!("rustc" => "values" of "feature" with "f_a" "f_b"))
+ .with_stderr_contains(x!("rustc" => "cfg" of "feature" with "f_a" "f_b"))
.run();
}
@@ -582,7 +528,36 @@ fn config_invalid() {
p.cargo("check")
.masquerade_as_nightly_cargo(&["check-cfg"])
- .with_stderr_contains("error: unstable check-cfg only takes `features`, `names`, `values` or `output` as valid inputs")
+ .with_stderr_contains("error:[..]`unstable.check-cfg` expected true/false[..]")
.with_status(101)
.run();
}
+
+#[cargo_test]
+fn config_feature_gate() {
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.1.0"
+
+ [features]
+ f_a = []
+ "#,
+ )
+ .file("src/main.rs", "fn main() {}")
+ .file(
+ ".cargo/config.toml",
+ r#"
+ [unstable]
+ check-cfg = true
+ "#,
+ )
+ .build();
+
+ p.cargo("check -v")
+ .with_stderr_does_not_contain("--check-cfg")
+ .run();
+}
diff --git a/src/tools/cargo/tests/testsuite/collisions.rs b/src/tools/cargo/tests/testsuite/collisions.rs
index 77e05dd9c..986619eb2 100644
--- a/src/tools/cargo/tests/testsuite/collisions.rs
+++ b/src/tools/cargo/tests/testsuite/collisions.rs
@@ -202,6 +202,7 @@ fn collision_doc_multiple_versions() {
[DOCUMENTING] bar v2.0.0
[FINISHED] [..]
[DOCUMENTING] foo v0.1.0 [..]
+[GENERATED] [CWD]/target/doc/foo/index.html
",
)
.run();
@@ -384,6 +385,7 @@ fn collision_doc_profile_split() {
[DOCUMENTING] pm v0.1.0 [..]
[DOCUMENTING] foo v0.1.0 [..]
[FINISHED] [..]
+[GENERATED] [CWD]/target/doc/foo/index.html
",
)
.run();
@@ -430,6 +432,7 @@ the same path; see <https://github.com/rust-lang/cargo/issues/6313>.
[CHECKING] bar v1.0.0
[DOCUMENTING] foo v0.1.0 [..]
[FINISHED] [..]
+[GENERATED] [CWD]/target/doc/foo/index.html
",
)
.run();
@@ -478,6 +481,7 @@ fn collision_doc_target() {
[CHECKING] bar v1.0.0
[DOCUMENTING] foo v0.1.0 [..]
[FINISHED] [..]
+[GENERATED] [CWD]/target/[..]/doc/foo/index.html
",
)
.run();
@@ -545,6 +549,8 @@ the same path; see <https://github.com/rust-lang/cargo/issues/6313>.
[DOCUMENTING] foo-macro v1.0.0 [..]
[DOCUMENTING] abc v1.0.0 [..]
[FINISHED] [..]
+[GENERATED] [CWD]/target/doc/abc/index.html
+[GENERATED] [CWD]/target/doc/foo_macro/index.html
")
.run();
}
diff --git a/src/tools/cargo/tests/testsuite/config.rs b/src/tools/cargo/tests/testsuite/config.rs
index 7078fc445..e5078bd8e 100644
--- a/src/tools/cargo/tests/testsuite/config.rs
+++ b/src/tools/cargo/tests/testsuite/config.rs
@@ -2,8 +2,9 @@
use cargo::core::{PackageIdSpec, Shell};
use cargo::util::config::{self, Config, Definition, JobsConfig, SslVersionConfig, StringList};
-use cargo::util::interning::InternedString;
-use cargo::util::toml::{self as cargo_toml, TomlDebugInfo, VecStringOrBool as VSOB};
+use cargo::util::toml::schema::TomlTrimPaths;
+use cargo::util::toml::schema::TomlTrimPathsValue;
+use cargo::util::toml::schema::{self as cargo_toml, TomlDebugInfo, VecStringOrBool as VSOB};
use cargo::CargoResult;
use cargo_test_support::compare;
use cargo_test_support::{panic_error, paths, project, symlink_supported, t};
@@ -21,6 +22,7 @@ pub struct ConfigBuilder {
unstable: Vec<String>,
config_args: Vec<String>,
cwd: Option<PathBuf>,
+ root: Option<PathBuf>,
enable_nightly_features: bool,
}
@@ -30,6 +32,7 @@ impl ConfigBuilder {
env: HashMap::new(),
unstable: Vec::new(),
config_args: Vec::new(),
+ root: None,
cwd: None,
enable_nightly_features: false,
}
@@ -60,8 +63,28 @@ impl ConfigBuilder {
}
/// Sets the current working directory where config files will be loaded.
+ ///
+ /// Default is the root from [`ConfigBuilder::root`] or [`paths::root`].
pub fn cwd(&mut self, path: impl AsRef<Path>) -> &mut Self {
- self.cwd = Some(paths::root().join(path.as_ref()));
+ let path = path.as_ref();
+ let cwd = self
+ .root
+ .as_ref()
+ .map_or_else(|| paths::root().join(path), |r| r.join(path));
+ self.cwd = Some(cwd);
+ self
+ }
+
+ /// Sets the test root directory.
+ ///
+ /// This generally should not be necessary. It is only useful if you want
+ /// to create a `Config` from within a thread. Since Cargo's testsuite
+ /// uses thread-local storage, this can be used to avoid accessing that
+ /// thread-local storage.
+ ///
+ /// Default is [`paths::root`].
+ pub fn root(&mut self, path: impl Into<PathBuf>) -> &mut Self {
+ self.root = Some(path.into());
self
}
@@ -72,14 +95,15 @@ impl ConfigBuilder {
/// Creates the `Config`, returning a Result.
pub fn build_err(&self) -> CargoResult<Config> {
- let output = Box::new(fs::File::create(paths::root().join("shell.out")).unwrap());
+ let root = self.root.clone().unwrap_or_else(|| paths::root());
+ let output = Box::new(fs::File::create(root.join("shell.out")).unwrap());
let shell = Shell::from_write(output);
- let cwd = self.cwd.clone().unwrap_or_else(|| paths::root());
- let homedir = paths::home();
+ let cwd = self.cwd.clone().unwrap_or_else(|| root.clone());
+ let homedir = root.join("home").join(".cargo");
let mut config = Config::new(shell, cwd, homedir);
config.nightly_features_allowed = self.enable_nightly_features || !self.unstable.is_empty();
config.set_env(self.env.clone());
- config.set_search_stop_path(paths::root());
+ config.set_search_stop_path(&root);
config.configure(
0,
false,
@@ -422,8 +446,8 @@ lto = false
p,
cargo_toml::TomlProfile {
lto: Some(cargo_toml::StringOrBool::Bool(false)),
- dir_name: Some(InternedString::new("without-lto")),
- inherits: Some(InternedString::new("dev")),
+ dir_name: Some(String::from("without-lto")),
+ inherits: Some(String::from("dev")),
..Default::default()
}
);
@@ -1503,7 +1527,7 @@ fn all_profile_options() {
let base_settings = cargo_toml::TomlProfile {
opt_level: Some(cargo_toml::TomlOptLevel("0".to_string())),
lto: Some(cargo_toml::StringOrBool::String("thin".to_string())),
- codegen_backend: Some(InternedString::new("example")),
+ codegen_backend: Some(String::from("example")),
codegen_units: Some(123),
debug: Some(cargo_toml::TomlDebugInfo::Limited),
split_debuginfo: Some("packed".to_string()),
@@ -1512,12 +1536,13 @@ fn all_profile_options() {
panic: Some("abort".to_string()),
overflow_checks: Some(true),
incremental: Some(true),
- dir_name: Some(InternedString::new("dir_name")),
- inherits: Some(InternedString::new("debug")),
+ dir_name: Some(String::from("dir_name")),
+ inherits: Some(String::from("debug")),
strip: Some(cargo_toml::StringOrBool::String("symbols".to_string())),
package: None,
build_override: None,
rustflags: None,
+ trim_paths: None,
};
let mut overrides = BTreeMap::new();
let key = cargo_toml::ProfilePackageSpec::Spec(PackageIdSpec::parse("foo").unwrap());
@@ -1705,3 +1730,63 @@ jobs = 2
JobsConfig::Integer(v) => assert_eq!(v, 2),
}
}
+
+#[cargo_test]
+fn trim_paths_parsing() {
+ let config = ConfigBuilder::new().build();
+ let p: cargo_toml::TomlProfile = config.get("profile.dev").unwrap();
+ assert_eq!(p.trim_paths, None);
+
+ let test_cases = [
+ (TomlTrimPathsValue::Diagnostics.into(), "diagnostics"),
+ (TomlTrimPathsValue::Macro.into(), "macro"),
+ (TomlTrimPathsValue::Object.into(), "object"),
+ ];
+ for (expected, val) in test_cases {
+ // env
+ let config = ConfigBuilder::new()
+ .env("CARGO_PROFILE_DEV_TRIM_PATHS", val)
+ .build();
+ let trim_paths: TomlTrimPaths = config.get("profile.dev.trim-paths").unwrap();
+ assert_eq!(trim_paths, expected, "failed to parse {val}");
+
+ // config.toml
+ let config = ConfigBuilder::new()
+ .config_arg(format!("profile.dev.trim-paths='{val}'"))
+ .build();
+ let trim_paths: TomlTrimPaths = config.get("profile.dev.trim-paths").unwrap();
+ assert_eq!(trim_paths, expected, "failed to parse {val}");
+ }
+
+ let test_cases = [(TomlTrimPaths::none(), false), (TomlTrimPaths::All, true)];
+
+ for (expected, val) in test_cases {
+ // env
+ let config = ConfigBuilder::new()
+ .env("CARGO_PROFILE_DEV_TRIM_PATHS", format!("{val}"))
+ .build();
+ let trim_paths: TomlTrimPaths = config.get("profile.dev.trim-paths").unwrap();
+ assert_eq!(trim_paths, expected, "failed to parse {val}");
+
+ // config.toml
+ let config = ConfigBuilder::new()
+ .config_arg(format!("profile.dev.trim-paths={val}"))
+ .build();
+ let trim_paths: TomlTrimPaths = config.get("profile.dev.trim-paths").unwrap();
+ assert_eq!(trim_paths, expected, "failed to parse {val}");
+ }
+
+ let expected = vec![
+ TomlTrimPathsValue::Diagnostics,
+ TomlTrimPathsValue::Macro,
+ TomlTrimPathsValue::Object,
+ ]
+ .into();
+ let val = r#"["diagnostics", "macro", "object"]"#;
+ // config.toml
+ let config = ConfigBuilder::new()
+ .config_arg(format!("profile.dev.trim-paths={val}"))
+ .build();
+ let trim_paths: TomlTrimPaths = config.get("profile.dev.trim-paths").unwrap();
+ assert_eq!(trim_paths, expected, "failed to parse {val}");
+}
diff --git a/src/tools/cargo/tests/testsuite/cross_compile.rs b/src/tools/cargo/tests/testsuite/cross_compile.rs
index 1bc0c277d..b57ba2c7a 100644
--- a/src/tools/cargo/tests/testsuite/cross_compile.rs
+++ b/src/tools/cargo/tests/testsuite/cross_compile.rs
@@ -411,92 +411,6 @@ fn linker() {
.run();
}
-#[cargo_test(nightly, reason = "plugins are unstable")]
-fn plugin_with_extra_dylib_dep() {
- if cross_compile::disabled() {
- return;
- }
-
- let foo = project()
- .file(
- "Cargo.toml",
- r#"
- [package]
- name = "foo"
- version = "0.0.1"
- authors = []
-
- [dependencies.bar]
- path = "../bar"
- "#,
- )
- .file(
- "src/main.rs",
- r#"
- #![feature(plugin)]
- #![plugin(bar)]
-
- fn main() {}
- "#,
- )
- .build();
- let _bar = project()
- .at("bar")
- .file(
- "Cargo.toml",
- r#"
- [package]
- name = "bar"
- version = "0.0.1"
- authors = []
-
- [lib]
- name = "bar"
- plugin = true
-
- [dependencies.baz]
- path = "../baz"
- "#,
- )
- .file(
- "src/lib.rs",
- r#"
- #![feature(rustc_private)]
-
- extern crate baz;
- extern crate rustc_driver;
-
- use rustc_driver::plugin::Registry;
-
- #[no_mangle]
- pub fn __rustc_plugin_registrar(reg: &mut Registry) {
- println!("{}", baz::baz());
- }
- "#,
- )
- .build();
- let _baz = project()
- .at("baz")
- .file(
- "Cargo.toml",
- r#"
- [package]
- name = "baz"
- version = "0.0.1"
- authors = []
-
- [lib]
- name = "baz"
- crate_type = ["dylib"]
- "#,
- )
- .file("src/lib.rs", "pub fn baz() -> i32 { 1 }")
- .build();
-
- let target = cross_compile::alternate();
- foo.cargo("build --target").arg(&target).run();
-}
-
#[cargo_test]
fn cross_tests() {
if !cross_compile::can_run_on_host() {
diff --git a/src/tools/cargo/tests/testsuite/custom_target.rs b/src/tools/cargo/tests/testsuite/custom_target.rs
index 491d3233c..a04029075 100644
--- a/src/tools/cargo/tests/testsuite/custom_target.rs
+++ b/src/tools/cargo/tests/testsuite/custom_target.rs
@@ -116,6 +116,8 @@ fn custom_target_dependency() {
}
#[cargo_test(nightly, reason = "requires features no_core, lang_items")]
+// This is randomly crashing in lld. See https://github.com/rust-lang/rust/issues/115985
+#[cfg_attr(all(windows, target_env = "gnu"), ignore = "windows-gnu lld crashing")]
fn custom_bin_target() {
let p = project()
.file(
diff --git a/src/tools/cargo/tests/testsuite/death.rs b/src/tools/cargo/tests/testsuite/death.rs
index f0e182d01..b61896dc9 100644
--- a/src/tools/cargo/tests/testsuite/death.rs
+++ b/src/tools/cargo/tests/testsuite/death.rs
@@ -1,12 +1,12 @@
//! Tests for ctrl-C handling.
+use cargo_test_support::{project, slow_cpu_multiplier};
use std::fs;
use std::io::{self, Read};
use std::net::TcpListener;
use std::process::{Child, Stdio};
use std::thread;
-
-use cargo_test_support::{project, slow_cpu_multiplier};
+use std::time;
#[cargo_test]
fn ctrl_c_kills_everyone() {
@@ -87,6 +87,155 @@ fn ctrl_c_kills_everyone() {
);
}
+#[cargo_test]
+fn kill_cargo_add_never_corrupts_cargo_toml() {
+ cargo_test_support::registry::init();
+ cargo_test_support::registry::Package::new("my-package", "0.1.1+my-package").publish();
+
+ let with_dependency = r#"
+[package]
+name = "foo"
+version = "0.0.1"
+authors = []
+
+[dependencies]
+my-package = "0.1.1"
+"#;
+ let without_dependency = r#"
+[package]
+name = "foo"
+version = "0.0.1"
+authors = []
+"#;
+
+ for sleep_time_ms in [30, 60, 90] {
+ let p = project()
+ .file("Cargo.toml", without_dependency)
+ .file("src/lib.rs", "")
+ .build();
+
+ let mut cargo = p.cargo("add").arg("my-package").build_command();
+ cargo
+ .stdin(Stdio::piped())
+ .stdout(Stdio::piped())
+ .stderr(Stdio::piped());
+
+ let mut child = cargo.spawn().unwrap();
+
+ thread::sleep(time::Duration::from_millis(sleep_time_ms));
+
+ assert!(child.kill().is_ok());
+ assert!(child.wait().is_ok());
+
+ // check the Cargo.toml
+ let contents = fs::read(p.root().join("Cargo.toml")).unwrap();
+
+ // not empty
+ assert_ne!(
+ contents, b"",
+ "Cargo.toml is empty, and should not be at {} milliseconds",
+ sleep_time_ms
+ );
+
+ // We should have the original Cargo.toml or the new one, nothing else.
+ if std::str::from_utf8(&contents)
+ .unwrap()
+ .contains("[dependencies]")
+ {
+ assert_eq!(
+ std::str::from_utf8(&contents).unwrap(),
+ with_dependency,
+ "Cargo.toml is with_dependency after add at {} milliseconds",
+ sleep_time_ms
+ );
+ } else {
+ assert_eq!(
+ std::str::from_utf8(&contents).unwrap(),
+ without_dependency,
+ "Cargo.toml is without_dependency after add at {} milliseconds",
+ sleep_time_ms
+ );
+ }
+ }
+}
+
+#[cargo_test]
+fn kill_cargo_remove_never_corrupts_cargo_toml() {
+ let with_dependency = r#"
+[package]
+name = "foo"
+version = "0.0.1"
+authors = []
+build = "build.rs"
+
+[dependencies]
+bar = "0.0.1"
+"#;
+ let without_dependency = r#"
+[package]
+name = "foo"
+version = "0.0.1"
+authors = []
+build = "build.rs"
+"#;
+
+ // This test depends on killing the cargo process at the right time to cause a failed write.
+ // Note that we're iterating and using the index as time in ms to sleep before killing the cargo process.
+ // If it is working correctly, we never fail, but can't hang out here all day...
+ // So we'll just run it a few times and hope for the best.
+ for sleep_time_ms in [30, 60, 90] {
+ // new basic project with a single dependency
+ let p = project()
+ .file("Cargo.toml", with_dependency)
+ .file("src/lib.rs", "")
+ .build();
+
+ // run cargo remove the dependency
+ let mut cargo = p.cargo("remove").arg("bar").build_command();
+ cargo
+ .stdin(Stdio::piped())
+ .stdout(Stdio::piped())
+ .stderr(Stdio::piped());
+
+ let mut child = cargo.spawn().unwrap();
+
+ thread::sleep(time::Duration::from_millis(sleep_time_ms));
+
+ assert!(child.kill().is_ok());
+ assert!(child.wait().is_ok());
+
+ // check the Cargo.toml
+ let contents = fs::read(p.root().join("Cargo.toml")).unwrap();
+
+ // not empty
+ assert_ne!(
+ contents, b"",
+ "Cargo.toml is empty, and should not be at {} milliseconds",
+ sleep_time_ms
+ );
+
+ // We should have the original Cargo.toml or the new one, nothing else.
+ if std::str::from_utf8(&contents)
+ .unwrap()
+ .contains("[dependencies]")
+ {
+ assert_eq!(
+ std::str::from_utf8(&contents).unwrap(),
+ with_dependency,
+ "Cargo.toml is not the same as the original at {} milliseconds",
+ sleep_time_ms
+ );
+ } else {
+ assert_eq!(
+ std::str::from_utf8(&contents).unwrap(),
+ without_dependency,
+ "Cargo.toml is not the same as expected at {} milliseconds",
+ sleep_time_ms
+ );
+ }
+ }
+}
+
#[cfg(unix)]
pub fn ctrl_c(child: &mut Child) {
let r = unsafe { libc::kill(-(child.id() as i32), libc::SIGINT) };
diff --git a/src/tools/cargo/tests/testsuite/doc.rs b/src/tools/cargo/tests/testsuite/doc.rs
index a16980912..65169d214 100644
--- a/src/tools/cargo/tests/testsuite/doc.rs
+++ b/src/tools/cargo/tests/testsuite/doc.rs
@@ -31,6 +31,7 @@ fn simple() {
[..] foo v0.0.1 ([CWD])
[..] foo v0.0.1 ([CWD])
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+[GENERATED] [CWD]/target/doc/foo/index.html
",
)
.run();
@@ -69,6 +70,7 @@ fn doc_twice() {
"\
[DOCUMENTING] foo v0.0.1 ([CWD])
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+[GENERATED] [CWD]/target/doc/foo/index.html
",
)
.run();
@@ -103,6 +105,7 @@ fn doc_deps() {
[..] bar v0.0.1 ([CWD]/bar)
[DOCUMENTING] foo v0.0.1 ([CWD])
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+[GENERATED] [CWD]/target/doc/foo/index.html
",
)
.run();
@@ -151,6 +154,7 @@ fn doc_no_deps() {
[CHECKING] bar v0.0.1 ([CWD]/bar)
[DOCUMENTING] foo v0.0.1 ([CWD])
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+[GENERATED] [CWD]/target/doc/foo/index.html
",
)
.run();
@@ -284,6 +288,8 @@ the same path; see <https://github.com/rust-lang/cargo/issues/6313>.
[DOCUMENTING] bar v0.1.0 ([ROOT]/foo/bar)
[DOCUMENTING] foo v0.1.0 ([ROOT]/foo/foo)
[FINISHED] [..]
+[GENERATED] [CWD]/target/doc/foo_lib/index.html
+[GENERATED] [CWD]/target/doc/foo_lib/index.html
",
)
.run();
@@ -398,6 +404,7 @@ fn doc_lib_bin_same_name_documents_lib() {
"\
[DOCUMENTING] foo v0.0.1 ([CWD])
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+[GENERATED] [CWD]/target/doc/foo/index.html
",
)
.run();
@@ -433,6 +440,7 @@ fn doc_lib_bin_same_name_documents_lib_when_requested() {
"\
[DOCUMENTING] foo v0.0.1 ([CWD])
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+[GENERATED] [CWD]/target/doc/foo/index.html
",
)
.run();
@@ -478,6 +486,7 @@ the same path; see <https://github.com/rust-lang/cargo/issues/6313>.
[CHECKING] foo v0.0.1 ([CWD])
[DOCUMENTING] foo v0.0.1 ([CWD])
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+[GENERATED] [CWD]/target/doc/foo/index.html
",
)
.run();
@@ -523,6 +532,7 @@ the same path; see <https://github.com/rust-lang/cargo/issues/6313>.
[CHECKING] foo v0.0.1 ([CWD])
[DOCUMENTING] foo v0.0.1 ([CWD])
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+[GENERATED] [CWD]/target/doc/foo/index.html
",
)
.run();
@@ -567,7 +577,9 @@ fn doc_lib_bin_example_same_name_documents_named_example_when_requested() {
"\
[CHECKING] foo v0.0.1 ([CWD])
[DOCUMENTING] foo v0.0.1 ([CWD])
-[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]",
+[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+[GENERATED] [CWD]/target/doc/ex1/index.html
+",
)
.run();
@@ -620,7 +632,10 @@ fn doc_lib_bin_example_same_name_documents_examples_when_requested() {
"\
[CHECKING] foo v0.0.1 ([CWD])
[DOCUMENTING] foo v0.0.1 ([CWD])
-[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]",
+[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+[GENERATED] [CWD]/target/doc/ex1/index.html
+[GENERATED] [CWD]/target/doc/ex2/index.html
+",
)
.run();
@@ -677,6 +692,7 @@ fn doc_dash_p() {
[..] b v0.0.1 ([CWD]/b)
[DOCUMENTING] a v0.0.1 ([CWD]/a)
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+[GENERATED] [CWD]/target/doc/a/index.html
",
)
.run();
@@ -704,6 +720,7 @@ fn doc_all_exclude() {
"\
[DOCUMENTING] bar v0.1.0 ([..])
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+[GENERATED] [CWD]/target/doc/bar/index.html
",
)
.run();
@@ -731,6 +748,7 @@ fn doc_all_exclude_glob() {
"\
[DOCUMENTING] bar v0.1.0 ([..])
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+[GENERATED] [CWD]/target/doc/bar/index.html
",
)
.run();
@@ -918,6 +936,7 @@ fn doc_release() {
[DOCUMENTING] foo v0.0.1 ([..])
[RUNNING] `rustdoc [..] src/lib.rs [..]`
[FINISHED] release [optimized] target(s) in [..]
+[GENERATED] [CWD]/target/doc/foo/index.html
",
)
.run();
@@ -1006,6 +1025,7 @@ fn features() {
[DOCUMENTING] bar v0.0.1 [..]
[DOCUMENTING] foo v0.0.1 [..]
[FINISHED] [..]
+[GENERATED] [CWD]/target/doc/foo/index.html
",
)
.run();
@@ -1020,6 +1040,7 @@ fn features() {
[DOCUMENTING] bar v0.0.1 [..]
[DOCUMENTING] foo v0.0.1 [..]
[FINISHED] [..]
+[GENERATED] [CWD]/target/doc/foo/index.html
",
)
.run();
@@ -1032,6 +1053,7 @@ fn features() {
[DOCUMENTING] bar v0.0.1 [..]
[DOCUMENTING] foo v0.0.1 [..]
[FINISHED] [..]
+[GENERATED] [CWD]/target/doc/foo/index.html
",
)
.run();
@@ -1202,6 +1224,7 @@ fn doc_virtual_manifest_one_project() {
"\
[DOCUMENTING] bar v0.1.0 ([..])
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+[GENERATED] [CWD]/target/doc/bar/index.html
",
)
.run();
@@ -1229,6 +1252,7 @@ fn doc_virtual_manifest_glob() {
"\
[DOCUMENTING] baz v0.1.0 ([..])
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+[GENERATED] [CWD]/target/doc/baz/index.html
",
)
.run();
@@ -1277,6 +1301,7 @@ the same path; see <https://github.com/rust-lang/cargo/issues/6313>.
[CHECKING] bar v0.1.0
[DOCUMENTING] bar v0.1.0 [..]
[FINISHED] [..]
+[GENERATED] [CWD]/target/doc/bar/index.html
",
)
.run();
@@ -1639,6 +1664,7 @@ fn doc_cap_lints() {
[CHECKING] a v0.5.0 ([..])
[DOCUMENTING] foo v0.0.1 ([..])
[FINISHED] dev [..]
+[GENERATED] [CWD]/target/doc/foo/index.html
",
)
.run();
@@ -1903,6 +1929,7 @@ fn bin_private_items() {
"\
[DOCUMENTING] foo v0.0.1 ([CWD])
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+[GENERATED] [CWD]/target/doc/foo/index.html
",
)
.run();
@@ -1963,6 +1990,7 @@ fn bin_private_items_deps() {
[CHECKING] bar v0.0.1 ([..])
[DOCUMENTING] foo v0.0.1 ([CWD])
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+[GENERATED] [CWD]/target/doc/foo/index.html
",
)
.run();
@@ -1997,6 +2025,7 @@ fn crate_versions() {
[DOCUMENTING] foo v1.2.4 [..]
[RUNNING] `rustdoc --crate-type lib --crate-name foo src/lib.rs [..]--crate-version 1.2.4`
[FINISHED] [..]
+[GENERATED] [CWD]/target/doc/foo/index.html
",
)
.run();
@@ -2406,7 +2435,8 @@ fn doc_fingerprint_unusual_behavior() {
p.cargo("doc")
.with_stderr(
"[DOCUMENTING] foo [..]\n\
- [FINISHED] [..]",
+ [FINISHED] [..]\n\
+ [GENERATED] [CWD]/target/doc/foo/index.html",
)
.run();
// This will delete somefile, but not .hidden.
@@ -2425,7 +2455,8 @@ fn doc_fingerprint_unusual_behavior() {
.masquerade_as_nightly_cargo(&["skip-rustdoc-fingerprint"])
.with_stderr(
"[DOCUMENTING] foo [..]\n\
- [FINISHED] [..]",
+ [FINISHED] [..]\n\
+ [GENERATED] [CWD]/target/doc/foo/index.html",
)
.run();
// Should not have deleted anything.
@@ -2467,6 +2498,8 @@ fn lib_before_bin() {
[RUNNING] `rustdoc --crate-type lib --crate-name foo src/lib.rs [..]
[RUNNING] `rustdoc --crate-type bin --crate-name somebin src/bin/somebin.rs [..]
[FINISHED] [..]
+[GENERATED] [CWD]/target/doc/foo/index.html
+[GENERATED] [CWD]/target/doc/somebin/index.html
",
)
.run();
@@ -2517,6 +2550,7 @@ fn doc_lib_false() {
[CHECKING] foo v0.1.0 [..]
[DOCUMENTING] foo v0.1.0 [..]
[FINISHED] [..]
+[GENERATED] [CWD]/target/doc/some_bin/index.html
",
)
.run();
@@ -2563,6 +2597,7 @@ fn doc_lib_false_dep() {
[CHECKING] bar v0.1.0 [..]
[DOCUMENTING] foo v0.1.0 [..]
[FINISHED] [..]
+[GENERATED] [CWD]/target/doc/foo/index.html
",
)
.run();
@@ -2587,7 +2622,8 @@ fn link_to_private_item() {
p.cargo("doc")
.with_stderr(
"[DOCUMENTING] foo [..]\n\
- [FINISHED] [..]",
+ [FINISHED] [..]\n\
+ [GENERATED] [CWD]/target/doc/foo/index.html",
)
.run();
}
diff --git a/src/tools/cargo/tests/testsuite/docscrape.rs b/src/tools/cargo/tests/testsuite/docscrape.rs
index c536a6738..d4d011ff3 100644
--- a/src/tools/cargo/tests/testsuite/docscrape.rs
+++ b/src/tools/cargo/tests/testsuite/docscrape.rs
@@ -26,13 +26,18 @@ fn basic() {
[SCRAPING] foo v0.0.1 ([CWD])
[DOCUMENTING] foo v0.0.1 ([CWD])
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+[GENERATED] [CWD]/target/doc/foo/index.html
",
)
.run();
p.cargo("doc -Zunstable-options -Z rustdoc-scrape-examples")
.masquerade_as_nightly_cargo(&["rustdoc-scrape-examples"])
- .with_stderr("[FINISHED] [..]")
+ .with_stderr(
+ "[FINISHED] [..]
+[GENERATED] [CWD]/target/doc/foo/index.html
+",
+ )
.run();
let doc_html = p.read_file("target/doc/foo/fn.foo.html");
@@ -311,6 +316,7 @@ fn cache() {
[SCRAPING] foo v0.0.1 ([CWD])
[DOCUMENTING] foo v0.0.1 ([CWD])
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+[GENERATED] [CWD]/target/doc/foo/index.html
",
)
.run();
@@ -320,6 +326,7 @@ fn cache() {
.with_stderr(
"\
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+[GENERATED] [CWD]/target/doc/foo/index.html
",
)
.run();
@@ -361,7 +368,9 @@ warning: failed to scan example \"ex2\" in package `foo` for example code usage
If an example should not be scanned, then consider adding `doc-scrape-examples = false` to its `[[example]]` definition in Cargo.toml
warning: `foo` (example \"ex2\") generated 1 warning
[DOCUMENTING] foo v0.0.1 ([CWD])
-[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]",
+[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+[GENERATED] [CWD]/target/doc/foo/index.html
+",
)
.run();
}
@@ -425,7 +434,9 @@ warning: failed to scan example \"ex1\" in package `foo` for example code usage
If an example should not be scanned, then consider adding `doc-scrape-examples = false` to its `[[example]]` definition in Cargo.toml
warning: `foo` (example \"ex1\") generated 1 warning
[DOCUMENTING] foo v0.0.1 ([CWD])
-[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]",
+[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+[GENERATED] [CWD]/target/doc/foo/index.html
+",
)
.run();
@@ -448,7 +459,9 @@ error: expected one of `!` or `::`, found `NOT`
| ^^^ expected one of `!` or `::`
[DOCUMENTING] foo v0.0.1 ([CWD])
-[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]",
+[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+[GENERATED] [CWD]/target/doc/foo/index.html
+",
)
.run();
@@ -499,7 +512,9 @@ warning: Rustdoc did not scrape the following examples because they require dev-
If you want Rustdoc to scrape these examples, then add `doc-scrape-examples = true`
to the [[example]] target configuration of at least one example.
[DOCUMENTING] foo v0.0.1 ([CWD])
-[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]",
+[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+[GENERATED] [CWD]/target/doc/foo/index.html
+",
)
.run();
@@ -513,7 +528,9 @@ warning: Rustdoc did not scrape the following examples because they require dev-
[DOCUMENTING] a v0.0.1 ([CWD]/a)
[SCRAPING] foo v0.0.1 ([CWD])
[DOCUMENTING] foo v0.0.1 ([CWD])
-[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]",
+[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+[GENERATED] [CWD]/target/doc/ex/index.html
+",
)
.run();
}
@@ -560,7 +577,9 @@ fn use_dev_deps_if_explicitly_enabled() {
[CHECKING] a v0.0.1 ([CWD]/a)
[SCRAPING] foo v0.0.1 ([CWD])
[DOCUMENTING] foo v0.0.1 ([CWD])
-[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]",
+[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+[GENERATED] [CWD]/target/doc/foo/index.html
+",
)
.run();
}
diff --git a/src/tools/cargo/tests/testsuite/features.rs b/src/tools/cargo/tests/testsuite/features.rs
index 557fab14a..4b7455c37 100644
--- a/src/tools/cargo/tests/testsuite/features.rs
+++ b/src/tools/cargo/tests/testsuite/features.rs
@@ -36,6 +36,37 @@ Caused by:
}
#[cargo_test]
+fn empty_feature_name() {
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+ authors = []
+
+ [features]
+ "" = []
+ "#,
+ )
+ .file("src/main.rs", "")
+ .build();
+
+ p.cargo("check")
+ .with_status(101)
+ .with_stderr(
+ "\
+[ERROR] failed to parse manifest at `[..]`
+
+Caused by:
+ feature name cannot be empty
+",
+ )
+ .run();
+}
+
+#[cargo_test]
fn same_name() {
// Feature with the same name as a dependency.
let p = project()
@@ -1144,6 +1175,61 @@ fn activating_feature_activates_dep() {
}
#[cargo_test]
+fn activating_feature_does_not_activate_transitive_dev_dependency() {
+ let p = project()
+ .no_manifest()
+ .file(
+ "a/Cargo.toml",
+ r#"
+ [package]
+ name = "a"
+ version = "0.0.0"
+ edition = "2021"
+
+ [features]
+ f = ["b/f"]
+
+ [dependencies]
+ b = { path = "../b" }
+ "#,
+ )
+ .file(
+ "b/Cargo.toml",
+ r#"
+ [package]
+ name = "b"
+ version = "0.0.0"
+ edition = "2021"
+
+ [features]
+ f = ["c/f"]
+
+ [dev-dependencies]
+ c = { path = "../c" }
+ "#,
+ )
+ .file(
+ "c/Cargo.toml",
+ r#"
+ [package]
+ name = "c"
+ version = "0.0.0"
+ edition = "2021"
+
+ [features]
+ f = []
+ "#,
+ )
+ .file("a/src/lib.rs", "")
+ .file("b/src/lib.rs", "")
+ .file("c/src/lib.rs", "compile_error!")
+ .build();
+
+ p.cargo("check --manifest-path a/Cargo.toml --features f")
+ .run();
+}
+
+#[cargo_test]
fn dep_feature_in_cmd_line() {
let p = project()
.file(
@@ -1990,7 +2076,7 @@ error: failed to parse manifest at `[ROOT]/foo/Cargo.toml`
Caused by:
invalid character `&` in feature `a&b` in package foo v0.1.0 ([ROOT]/foo), \
- characters must be Unicode XID characters, `+`, or `.` \
+ characters must be Unicode XID characters, '-', `+`, or `.` \
(numbers, `+`, `-`, `_`, `.`, or most letters)
",
)
diff --git a/src/tools/cargo/tests/testsuite/features2.rs b/src/tools/cargo/tests/testsuite/features2.rs
index 9238de2c6..125a293a0 100644
--- a/src/tools/cargo/tests/testsuite/features2.rs
+++ b/src/tools/cargo/tests/testsuite/features2.rs
@@ -1807,7 +1807,7 @@ fn shared_dep_same_but_dependencies() {
[COMPILING] dep [..]
[COMPILING] bin2 [..]
[COMPILING] bin1 [..]
-warning: feat: enabled
+warning: bin2@0.1.0: feat: enabled
[FINISHED] [..]
",
)
@@ -1823,7 +1823,7 @@ warning: feat: enabled
[FRESH] subdep [..]
[FRESH] dep [..]
[FRESH] bin1 [..]
-warning: feat: enabled
+warning: bin2@0.1.0: feat: enabled
[FRESH] bin2 [..]
[FINISHED] [..]
",
@@ -1955,6 +1955,7 @@ fn doc_optional() {
[CHECKING] bar v1.0.0
[DOCUMENTING] foo v0.1.0 [..]
[FINISHED] [..]
+[GENERATED] [CWD]/target/doc/foo/index.html
",
)
.run();
diff --git a/src/tools/cargo/tests/testsuite/glob_targets.rs b/src/tools/cargo/tests/testsuite/glob_targets.rs
index 8021dffa9..1eed4b1fa 100644
--- a/src/tools/cargo/tests/testsuite/glob_targets.rs
+++ b/src/tools/cargo/tests/testsuite/glob_targets.rs
@@ -137,6 +137,7 @@ fn doc_bin() {
[DOCUMENTING] foo v0.0.1 ([CWD])
[RUNNING] `rustdoc --crate-type bin --crate-name bin1 [..]`
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+[GENERATED] [CWD]/target/doc/bin1/index.html
",
)
.run();
@@ -407,6 +408,7 @@ fn rustdoc_example() {
[DOCUMENTING] foo v0.0.1 ([CWD])
[RUNNING] `rustdoc --crate-type bin --crate-name example1 [..]`
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+[GENERATED] [CWD]/target/doc/example1/index.html
",
)
.run();
@@ -421,6 +423,7 @@ fn rustdoc_bin() {
[DOCUMENTING] foo v0.0.1 ([CWD])
[RUNNING] `rustdoc --crate-type bin --crate-name bin1 [..]`
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+[GENERATED] [CWD]/target/doc/bin1/index.html
",
)
.run();
@@ -435,6 +438,7 @@ fn rustdoc_bench() {
[DOCUMENTING] foo v0.0.1 ([CWD])
[RUNNING] `rustdoc --crate-type bin --crate-name bench1 [..]`
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+[GENERATED] [CWD]/target/doc/bench1/index.html
",
)
.run();
@@ -449,6 +453,7 @@ fn rustdoc_test() {
[DOCUMENTING] foo v0.0.1 ([CWD])
[RUNNING] `rustdoc --crate-type bin --crate-name test1 [..]`
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+[GENERATED] [CWD]/target/doc/test1/index.html
",
)
.run();
diff --git a/src/tools/cargo/tests/testsuite/install.rs b/src/tools/cargo/tests/testsuite/install.rs
index 0a3670e6c..fd53b607b 100644
--- a/src/tools/cargo/tests/testsuite/install.rs
+++ b/src/tools/cargo/tests/testsuite/install.rs
@@ -58,6 +58,28 @@ fn simple() {
}
#[cargo_test]
+fn install_the_same_version_twice() {
+ pkg("foo", "0.0.1");
+
+ cargo_process("install foo foo")
+ .with_stderr(
+ "\
+[UPDATING] `[..]` index
+[DOWNLOADING] crates ...
+[DOWNLOADED] foo v0.0.1 (registry [..])
+[INSTALLING] foo v0.0.1
+[COMPILING] foo v0.0.1
+[FINISHED] release [optimized] target(s) in [..]
+[INSTALLING] [CWD]/home/.cargo/bin/foo[EXE]
+[INSTALLED] package `foo v0.0.1` (executable `foo[EXE]`)
+[WARNING] be sure to add `[..]` to your PATH to be able to run the installed binaries
+",
+ )
+ .run();
+ assert_has_installed_exe(cargo_home(), "foo");
+}
+
+#[cargo_test]
fn toolchain() {
pkg("foo", "0.0.1");
@@ -1614,7 +1636,7 @@ fn inline_version_without_name() {
cargo_process("install @0.1.1")
.with_status(1)
.with_stderr(
- "error: invalid value '@0.1.1' for '[crate]...': missing crate name before '@'
+ "error: invalid value '@0.1.1' for '[CRATE[@<VER>]]...': missing crate name before '@'
For more information, try '--help'.
",
@@ -1844,7 +1866,9 @@ fn install_empty_argument() {
cargo_process("install")
.arg("")
.with_status(1)
- .with_stderr_contains("[ERROR] invalid value '' for '[crate]...': crate name is empty")
+ .with_stderr_contains(
+ "[ERROR] invalid value '' for '[CRATE[@<VER>]]...': crate name is empty",
+ )
.run();
}
@@ -2455,7 +2479,7 @@ error: unexpected argument '--release' found
tip: `--release` is the default for `cargo install`; instead `--debug` is supported
-Usage: cargo[EXE] install [OPTIONS] [crate]...
+Usage: cargo[EXE] install [OPTIONS] [CRATE[@<VER>]]...
For more information, try '--help'.
",
@@ -2463,3 +2487,23 @@ For more information, try '--help'.
.with_status(1)
.run();
}
+
+#[cargo_test]
+fn install_incompat_msrv() {
+ Package::new("foo", "0.1.0")
+ .file("src/main.rs", "fn main() {}")
+ .rust_version("1.30")
+ .publish();
+ Package::new("foo", "0.2.0")
+ .file("src/main.rs", "fn main() {}")
+ .rust_version("1.9876.0")
+ .publish();
+
+ cargo_process("install foo")
+ .with_stderr("\
+[UPDATING] `dummy-registry` index
+[ERROR] cannot install package `foo 0.2.0`, it requires rustc 1.9876.0 or newer, while the currently active rustc version is [..]
+`foo 0.1.0` supports rustc 1.30
+")
+ .with_status(101).run();
+}
diff --git a/src/tools/cargo/tests/testsuite/install_upgrade.rs b/src/tools/cargo/tests/testsuite/install_upgrade.rs
index 580117f5c..fe4f8c6c7 100644
--- a/src/tools/cargo/tests/testsuite/install_upgrade.rs
+++ b/src/tools/cargo/tests/testsuite/install_upgrade.rs
@@ -230,7 +230,7 @@ fn ambiguous_version_no_longer_allowed() {
cargo_process("install foo --version=1.0")
.with_stderr(
"\
-[ERROR] invalid value '1.0' for '--version <VERSION>': cannot parse '1.0' as a SemVer version
+[ERROR] invalid value '1.0' for '--version <VERSION>': unexpected end of input while parsing minor version number
tip: if you want to specify SemVer range, add an explicit qualifier, like '^1.0'
diff --git a/src/tools/cargo/tests/testsuite/list_availables.rs b/src/tools/cargo/tests/testsuite/list_availables.rs
index fe635a19b..ebd6e9c1c 100644
--- a/src/tools/cargo/tests/testsuite/list_availables.rs
+++ b/src/tools/cargo/tests/testsuite/list_availables.rs
@@ -59,7 +59,7 @@ Available binaries:
.with_stderr(
"\
error: \"--bench\" takes one argument.
-Available benches:
+Available bench targets:
bench1
bench2
@@ -75,7 +75,7 @@ Available benches:
.with_stderr(
"\
error: \"--test\" takes one argument.
-Available tests:
+Available test targets:
test1
test2
@@ -139,7 +139,7 @@ No binaries available.
.with_stderr(
"\
error: \"--bench\" takes one argument.
-No benches available.
+No bench targets available.
",
)
@@ -153,7 +153,7 @@ No benches available.
.with_stderr(
"\
error: \"--test\" takes one argument.
-No tests available.
+No test targets available.
",
)
diff --git a/src/tools/cargo/tests/testsuite/main.rs b/src/tools/cargo/tests/testsuite/main.rs
index 8279f5818..07f749e34 100644
--- a/src/tools/cargo/tests/testsuite/main.rs
+++ b/src/tools/cargo/tests/testsuite/main.rs
@@ -17,6 +17,7 @@ mod build_plan;
mod build_script;
mod build_script_env;
mod build_script_extra_link_arg;
+mod cache_lock;
mod cache_messages;
mod cargo;
mod cargo_add;
@@ -131,12 +132,12 @@ mod patch;
mod path;
mod paths;
mod pkgid;
-mod plugins;
mod proc_macro;
mod profile_config;
mod profile_custom;
mod profile_overrides;
mod profile_targets;
+mod profile_trim_paths;
mod profiles;
mod progress;
mod pub_priv;
diff --git a/src/tools/cargo/tests/testsuite/metadata.rs b/src/tools/cargo/tests/testsuite/metadata.rs
index fbead4dea..888cdce8c 100644
--- a/src/tools/cargo/tests/testsuite/metadata.rs
+++ b/src/tools/cargo/tests/testsuite/metadata.rs
@@ -4257,3 +4257,285 @@ fn workspace_metadata_with_dependencies_no_deps_artifact() {
)
.run();
}
+
+#[cargo_test]
+fn versionless_packages() {
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [workspace]
+ members = ["bar", "baz"]
+ "#,
+ )
+ .file(
+ "bar/Cargo.toml",
+ r#"
+ [package]
+ name = "bar"
+
+ [dependencies]
+ foobar = "0.0.1"
+ baz = { path = "../baz/" }
+ "#,
+ )
+ .file("bar/src/lib.rs", "")
+ .file(
+ "baz/Cargo.toml",
+ r#"
+ [package]
+ name = "baz"
+
+ [dependencies]
+ foobar = "0.0.1"
+ "#,
+ )
+ .file("baz/src/lib.rs", "")
+ .build();
+ Package::new("foobar", "0.0.1").publish();
+
+ p.cargo("metadata -q --format-version 1")
+ .with_json(
+ r#"
+{
+ "packages": [
+ {
+ "name": "bar",
+ "version": "0.0.0",
+ "id": "bar 0.0.0 [..]",
+ "license": null,
+ "license_file": null,
+ "description": null,
+ "source": null,
+ "dependencies": [
+ {
+ "name": "baz",
+ "source": null,
+ "req": "*",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null,
+ "path": "[..]/baz"
+ },
+ {
+ "name": "foobar",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.0.1",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "bar",
+ "src_path": "[..]/bar/src/lib.rs",
+ "edition": "2015",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ }
+ ],
+ "features": {},
+ "manifest_path": "[..]/bar/Cargo.toml",
+ "metadata": null,
+ "publish": [],
+ "authors": [],
+ "categories": [],
+ "keywords": [],
+ "readme": null,
+ "repository": null,
+ "homepage": null,
+ "documentation": null,
+ "edition": "2015",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "baz",
+ "version": "0.0.0",
+ "id": "baz 0.0.0 [..]",
+ "license": null,
+ "license_file": null,
+ "description": null,
+ "source": null,
+ "dependencies": [
+ {
+ "name": "foobar",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.0.1",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "baz",
+ "src_path": "[..]/baz/src/lib.rs",
+ "edition": "2015",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ }
+ ],
+ "features": {},
+ "manifest_path": "[..]/baz/Cargo.toml",
+ "metadata": null,
+ "publish": [],
+ "authors": [],
+ "categories": [],
+ "keywords": [],
+ "readme": null,
+ "repository": null,
+ "homepage": null,
+ "documentation": null,
+ "edition": "2015",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "foobar",
+ "version": "0.0.1",
+ "id": "foobar 0.0.1 [..]",
+ "license": null,
+ "license_file": null,
+ "description": null,
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "foobar",
+ "src_path": "[..]/foobar-0.0.1/src/lib.rs",
+ "edition": "2015",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ }
+ ],
+ "features": {},
+ "manifest_path": "[..]/foobar-0.0.1/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [],
+ "categories": [],
+ "keywords": [],
+ "readme": null,
+ "repository": null,
+ "homepage": null,
+ "documentation": null,
+ "edition": "2015",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ }
+ ],
+ "workspace_members": [
+ "bar 0.0.0 [..]",
+ "baz 0.0.0 [..]"
+ ],
+ "workspace_default_members": [
+ "bar 0.0.0 [..]",
+ "baz 0.0.0 [..]"
+ ],
+ "resolve": {
+ "nodes": [
+ {
+ "id": "bar 0.0.0 [..]",
+ "dependencies": [
+ "baz 0.0.0 [..]",
+ "foobar 0.0.1 [..]"
+ ],
+ "deps": [
+ {
+ "name": "baz",
+ "pkg": "baz 0.0.0 [..]",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "foobar",
+ "pkg": "foobar 0.0.1 [..]",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ }
+ ],
+ "features": []
+ },
+ {
+ "id": "baz 0.0.0 [..]",
+ "dependencies": [
+ "foobar 0.0.1 [..]"
+ ],
+ "deps": [
+ {
+ "name": "foobar",
+ "pkg": "foobar 0.0.1 [..]",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ }
+ ],
+ "features": []
+ },
+ {
+ "id": "foobar 0.0.1 [..]",
+ "dependencies": [],
+ "deps": [],
+ "features": []
+ }
+ ],
+ "root": null
+ },
+ "target_directory": "[..]/foo/target",
+ "version": 1,
+ "workspace_root": "[..]",
+ "metadata": null
+}
+"#,
+ )
+ .run();
+}
diff --git a/src/tools/cargo/tests/testsuite/multitarget.rs b/src/tools/cargo/tests/testsuite/multitarget.rs
index 5f3543f01..30be9e97d 100644
--- a/src/tools/cargo/tests/testsuite/multitarget.rs
+++ b/src/tools/cargo/tests/testsuite/multitarget.rs
@@ -111,6 +111,34 @@ fn simple_doc() {
}
#[cargo_test]
+fn simple_doc_open() {
+ if cross_compile::disabled() {
+ return;
+ }
+ let t1 = cross_compile::alternate();
+ let t2 = rustc_host();
+ let p = project()
+ .file("Cargo.toml", &basic_manifest("foo", "1.0.0"))
+ .file("src/lib.rs", "//! empty lib")
+ .build();
+
+ p.cargo("doc")
+ .arg("--open")
+ .arg("--target")
+ .arg(&t1)
+ .arg("--target")
+ .arg(&t2)
+ .with_stderr(
+ "\
+[DOCUMENTING] foo v1.0.0 ([..])
+[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+[ERROR] only one `--target` argument is supported",
+ )
+ .with_status(101)
+ .run();
+}
+
+#[cargo_test]
fn simple_check() {
if cross_compile::disabled() {
return;
diff --git a/src/tools/cargo/tests/testsuite/new.rs b/src/tools/cargo/tests/testsuite/new.rs
index 91a2871e9..a34169e9d 100644
--- a/src/tools/cargo/tests/testsuite/new.rs
+++ b/src/tools/cargo/tests/testsuite/new.rs
@@ -124,7 +124,7 @@ fn no_argument() {
.with_stderr_contains(
"\
error: the following required arguments were not provided:
- <path>
+ <PATH>
",
)
.run();
@@ -451,6 +451,7 @@ fn non_ascii_name() {
"\
[WARNING] the name `Привет` contains non-ASCII characters
Non-ASCII crate names are not supported by Rust.
+[WARNING] the name `Привет` is not snake_case or kebab-case which is recommended for package names, consider `привет`
[CREATED] binary (application) `Привет` package
",
)
@@ -502,6 +503,29 @@ or change the name in Cargo.toml with:
}
#[cargo_test]
+fn non_snake_case_name() {
+ cargo_process("new UPPERcase_name")
+ .with_stderr(
+ "\
+[WARNING] the name `UPPERcase_name` is not snake_case or kebab-case which is recommended for package names, consider `uppercase_name`
+[CREATED] binary (application) `UPPERcase_name` package
+",
+ )
+ .run();
+}
+
+#[cargo_test]
+fn kebab_case_name_is_accepted() {
+ cargo_process("new kebab-case-is-valid")
+ .with_stderr(
+ "\
+[CREATED] binary (application) `kebab-case-is-valid` package
+",
+ )
+ .run();
+}
+
+#[cargo_test]
fn git_default_branch() {
// Check for init.defaultBranch support.
create_default_gitconfig();
diff --git a/src/tools/cargo/tests/testsuite/out_dir.rs b/src/tools/cargo/tests/testsuite/out_dir.rs
index fe647f56e..83621a2d2 100644
--- a/src/tools/cargo/tests/testsuite/out_dir.rs
+++ b/src/tools/cargo/tests/testsuite/out_dir.rs
@@ -281,6 +281,29 @@ fn cargo_build_out_dir() {
);
}
+#[cargo_test]
+fn unsupported_short_out_dir_flag() {
+ let p = project()
+ .file("src/main.rs", r#"fn main() { println!("Hello, World!") }"#)
+ .build();
+
+ p.cargo("build -Z unstable-options -O")
+ .masquerade_as_nightly_cargo(&["out-dir"])
+ .with_stderr(
+ "\
+error: unexpected argument '-O' found
+
+ tip: a similar argument exists: '--out-dir'
+
+Usage: cargo[EXE] build [OPTIONS]
+
+For more information, try '--help'.
+",
+ )
+ .with_status(1)
+ .run();
+}
+
fn check_dir_contents(
out_dir: &Path,
expected_linux: &[&str],
diff --git a/src/tools/cargo/tests/testsuite/package.rs b/src/tools/cargo/tests/testsuite/package.rs
index 010523fda..4ec4fc0d6 100644
--- a/src/tools/cargo/tests/testsuite/package.rs
+++ b/src/tools/cargo/tests/testsuite/package.rs
@@ -1359,7 +1359,7 @@ Caused by:
failed to parse the `edition` key
Caused by:
- supported edition values are `2015`, `2018`, or `2021`, but `chicken` is unknown
+ supported edition values are `2015`, `2018`, `2021`, or `2024`, but `chicken` is unknown
"
.to_string(),
)
@@ -1391,7 +1391,7 @@ Caused by:
failed to parse the `edition` key
Caused by:
- this version of Cargo is older than the `2038` edition, and only supports `2015`, `2018`, and `2021` editions.
+ this version of Cargo is older than the `2038` edition, and only supports `2015`, `2018`, `2021`, and `2024` editions.
"
.to_string(),
)
@@ -3095,3 +3095,40 @@ src/main.rs
&[],
);
}
+
+#[cargo_test]
+fn versionless_package() {
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ description = "foo"
+ "#,
+ )
+ .file("src/main.rs", r#"fn main() { println!("hello"); }"#)
+ .build();
+
+ p.cargo("package")
+ .with_stderr(
+ "\
+warning: manifest has no license, license-file, documentation, homepage or repository.
+See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info.
+ Packaging foo v0.0.0 ([CWD])
+ Verifying foo v0.0.0 ([CWD])
+ Compiling foo v0.0.0 ([CWD]/target/package/foo-0.0.0)
+ Finished dev [unoptimized + debuginfo] target(s) in [..]s
+ Packaged 4 files, [..]B ([..]B compressed)
+",
+ )
+ .run();
+
+ let f = File::open(&p.root().join("target/package/foo-0.0.0.crate")).unwrap();
+ validate_crate_contents(
+ f,
+ "foo-0.0.0.crate",
+ &["Cargo.lock", "Cargo.toml", "Cargo.toml.orig", "src/main.rs"],
+ &[],
+ );
+}
diff --git a/src/tools/cargo/tests/testsuite/plugins.rs b/src/tools/cargo/tests/testsuite/plugins.rs
deleted file mode 100644
index 331ba32e0..000000000
--- a/src/tools/cargo/tests/testsuite/plugins.rs
+++ /dev/null
@@ -1,421 +0,0 @@
-//! Tests for rustc plugins.
-
-use cargo_test_support::rustc_host;
-use cargo_test_support::{basic_manifest, project};
-
-#[cargo_test(nightly, reason = "plugins are unstable")]
-fn plugin_to_the_max() {
- let foo = project()
- .file(
- "Cargo.toml",
- r#"
- [package]
- name = "foo"
- version = "0.0.1"
- authors = []
-
- [lib]
- name = "foo_lib"
-
- [dependencies.bar]
- path = "../bar"
- "#,
- )
- .file(
- "src/main.rs",
- r#"
- #![feature(plugin)]
- #![plugin(bar)]
- extern crate foo_lib;
-
- fn main() { foo_lib::foo(); }
- "#,
- )
- .file(
- "src/foo_lib.rs",
- r#"
- #![feature(plugin)]
- #![plugin(bar)]
-
- pub fn foo() {}
- "#,
- )
- .build();
- let _bar = project()
- .at("bar")
- .file(
- "Cargo.toml",
- r#"
- [package]
- name = "bar"
- version = "0.0.1"
- authors = []
-
- [lib]
- name = "bar"
- plugin = true
-
- [dependencies.baz]
- path = "../baz"
- "#,
- )
- .file(
- "src/lib.rs",
- r#"
- #![feature(rustc_private)]
-
- extern crate baz;
- extern crate rustc_driver;
-
- use rustc_driver::plugin::Registry;
-
- #[no_mangle]
- pub fn __rustc_plugin_registrar(_reg: &mut Registry) {
- println!("{}", baz::baz());
- }
- "#,
- )
- .build();
- let _baz = project()
- .at("baz")
- .file(
- "Cargo.toml",
- r#"
- [package]
- name = "baz"
- version = "0.0.1"
- authors = []
-
- [lib]
- name = "baz"
- crate_type = ["dylib"]
- "#,
- )
- .file("src/lib.rs", "pub fn baz() -> i32 { 1 }")
- .build();
-
- foo.cargo("build").run();
- foo.cargo("doc").run();
-}
-
-#[cargo_test(nightly, reason = "plugins are unstable")]
-fn plugin_with_dynamic_native_dependency() {
- let build = project()
- .at("builder")
- .file(
- "Cargo.toml",
- r#"
- [package]
- name = "builder"
- version = "0.0.1"
- authors = []
-
- [lib]
- name = "builder"
- crate-type = ["dylib"]
- "#,
- )
- .file("src/lib.rs", "#[no_mangle] pub extern fn foo() {}")
- .build();
-
- let foo = project()
- .file(
- "Cargo.toml",
- r#"
- [package]
- name = "foo"
- version = "0.0.1"
- authors = []
-
- [dependencies.bar]
- path = "bar"
- "#,
- )
- .file(
- "src/main.rs",
- r#"
- #![feature(plugin)]
- #![plugin(bar)]
-
- fn main() {}
- "#,
- )
- .file(
- "bar/Cargo.toml",
- r#"
- [package]
- name = "bar"
- version = "0.0.1"
- authors = []
- build = 'build.rs'
-
- [lib]
- name = "bar"
- plugin = true
- "#,
- )
- .file(
- "bar/build.rs",
- r#"
- use std::env;
- use std::fs;
- use std::path::PathBuf;
-
- fn main() {
- let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
- let root = PathBuf::from(env::var("BUILDER_ROOT").unwrap());
- let file = format!("{}builder{}",
- env::consts::DLL_PREFIX,
- env::consts::DLL_SUFFIX);
- let src = root.join(&file);
- let dst = out_dir.join(&file);
- fs::copy(src, dst).unwrap();
- if cfg!(target_env = "msvc") {
- fs::copy(root.join("builder.dll.lib"),
- out_dir.join("builder.dll.lib")).unwrap();
- }
- println!("cargo:rustc-flags=-L {}", out_dir.display());
- }
- "#,
- )
- .file(
- "bar/src/lib.rs",
- r#"
- #![feature(rustc_private)]
-
- extern crate rustc_driver;
- use rustc_driver::plugin::Registry;
-
- #[cfg_attr(not(target_env = "msvc"), link(name = "builder"))]
- #[cfg_attr(target_env = "msvc", link(name = "builder.dll"))]
- extern { fn foo(); }
-
- #[no_mangle]
- pub fn __rustc_plugin_registrar(_reg: &mut Registry) {
- unsafe { foo() }
- }
- "#,
- )
- .build();
-
- build.cargo("build").run();
-
- let root = build.root().join("target").join("debug");
- foo.cargo("build -v").env("BUILDER_ROOT", root).run();
-}
-
-#[cargo_test]
-fn plugin_integration() {
- let p = project()
- .file(
- "Cargo.toml",
- r#"
- [package]
- name = "foo"
- version = "0.0.1"
- authors = []
- build = "build.rs"
-
- [lib]
- name = "foo"
- plugin = true
- doctest = false
- "#,
- )
- .file("build.rs", "fn main() {}")
- .file("src/lib.rs", "")
- .file("tests/it_works.rs", "")
- .build();
-
- p.cargo("test -v").run();
-}
-
-#[cargo_test]
-fn doctest_a_plugin() {
- let p = project()
- .file(
- "Cargo.toml",
- r#"
- [package]
- name = "foo"
- version = "0.0.1"
- authors = []
-
- [dependencies]
- bar = { path = "bar" }
- "#,
- )
- .file("src/lib.rs", "#[macro_use] extern crate bar;")
- .file(
- "bar/Cargo.toml",
- r#"
- [package]
- name = "bar"
- version = "0.0.1"
- authors = []
-
- [lib]
- name = "bar"
- plugin = true
- "#,
- )
- .file("bar/src/lib.rs", "pub fn bar() {}")
- .build();
-
- p.cargo("test -v").run();
-}
-
-// See #1515
-#[cargo_test]
-fn native_plugin_dependency_with_custom_linker() {
- let target = rustc_host();
-
- let _foo = project()
- .file(
- "Cargo.toml",
- r#"
- [package]
- name = "foo"
- version = "0.0.1"
- authors = []
-
- [lib]
- plugin = true
- "#,
- )
- .file("src/lib.rs", "")
- .build();
-
- let bar = project()
- .at("bar")
- .file(
- "Cargo.toml",
- r#"
- [package]
- name = "bar"
- version = "0.0.1"
- authors = []
-
- [dependencies.foo]
- path = "../foo"
- "#,
- )
- .file("src/lib.rs", "")
- .file(
- ".cargo/config",
- &format!(
- r#"
- [target.{}]
- linker = "nonexistent-linker"
- "#,
- target
- ),
- )
- .build();
-
- bar.cargo("build --verbose")
- .with_status(101)
- .with_stderr_contains(
- "\
-[COMPILING] foo v0.0.1 ([..])
-[RUNNING] `rustc [..] -C linker=nonexistent-linker [..]`
-[ERROR] [..]linker[..]
-",
- )
- .run();
-}
-
-#[cargo_test(nightly, reason = "requires rustc_private")]
-fn panic_abort_plugins() {
- let p = project()
- .file(
- "Cargo.toml",
- r#"
- [package]
- name = "foo"
- version = "0.0.1"
- authors = []
-
- [profile.dev]
- panic = 'abort'
-
- [dependencies]
- bar = { path = "bar" }
- "#,
- )
- .file("src/lib.rs", "")
- .file(
- "bar/Cargo.toml",
- r#"
- [package]
- name = "bar"
- version = "0.0.1"
- authors = []
-
- [lib]
- plugin = true
- "#,
- )
- .file(
- "bar/src/lib.rs",
- r#"
- #![feature(rustc_private)]
- extern crate rustc_ast;
- extern crate rustc_driver;
- "#,
- )
- .build();
-
- p.cargo("build").run();
-}
-
-#[cargo_test(nightly, reason = "requires rustc_private")]
-fn shared_panic_abort_plugins() {
- let p = project()
- .file(
- "Cargo.toml",
- r#"
- [package]
- name = "foo"
- version = "0.0.1"
- authors = []
-
- [profile.dev]
- panic = 'abort'
-
- [dependencies]
- bar = { path = "bar" }
- baz = { path = "baz" }
- "#,
- )
- .file("src/lib.rs", "extern crate baz;")
- .file(
- "bar/Cargo.toml",
- r#"
- [package]
- name = "bar"
- version = "0.0.1"
- authors = []
-
- [lib]
- plugin = true
-
- [dependencies]
- baz = { path = "../baz" }
- "#,
- )
- .file(
- "bar/src/lib.rs",
- r#"
- #![feature(rustc_private)]
- extern crate rustc_ast;
- extern crate rustc_driver;
- extern crate baz;
- "#,
- )
- .file("baz/Cargo.toml", &basic_manifest("baz", "0.0.1"))
- .file("baz/src/lib.rs", "")
- .build();
-
- p.cargo("build -v").run();
-}
diff --git a/src/tools/cargo/tests/testsuite/proc_macro.rs b/src/tools/cargo/tests/testsuite/proc_macro.rs
index 7d6f6ba86..cabf251a0 100644
--- a/src/tools/cargo/tests/testsuite/proc_macro.rs
+++ b/src/tools/cargo/tests/testsuite/proc_macro.rs
@@ -202,52 +202,6 @@ fn impl_and_derive() {
p.cargo("run").with_stdout("X { success: true }").run();
}
-#[cargo_test(nightly, reason = "plugins are unstable")]
-fn plugin_and_proc_macro() {
- let p = project()
- .file(
- "Cargo.toml",
- r#"
- [package]
- name = "foo"
- version = "0.0.1"
- authors = []
-
- [lib]
- plugin = true
- proc-macro = true
- "#,
- )
- .file(
- "src/lib.rs",
- r#"
- #![feature(rustc_private)]
- #![feature(proc_macro, proc_macro_lib)]
-
- extern crate rustc_driver;
- use rustc_driver::plugin::Registry;
-
- extern crate proc_macro;
- use proc_macro::TokenStream;
-
- #[no_mangle]
- pub fn __rustc_plugin_registrar(reg: &mut Registry) {}
-
- #[proc_macro_derive(Questionable)]
- pub fn questionable(input: TokenStream) -> TokenStream {
- input
- }
- "#,
- )
- .build();
-
- let msg = " `lib.plugin` and `lib.proc-macro` cannot both be `true`";
- p.cargo("check")
- .with_status(101)
- .with_stderr_contains(msg)
- .run();
-}
-
#[cargo_test]
fn proc_macro_doctest() {
let foo = project()
diff --git a/src/tools/cargo/tests/testsuite/profile_config.rs b/src/tools/cargo/tests/testsuite/profile_config.rs
index 143c050f9..710a0d8ef 100644
--- a/src/tools/cargo/tests/testsuite/profile_config.rs
+++ b/src/tools/cargo/tests/testsuite/profile_config.rs
@@ -1,6 +1,6 @@
//! Tests for profiles defined in config files.
-use cargo::util::toml::TomlDebugInfo;
+use cargo::util::toml::schema::TomlDebugInfo;
use cargo_test_support::paths::CargoPathExt;
use cargo_test_support::registry::Package;
use cargo_test_support::{basic_lib_manifest, paths, project};
diff --git a/src/tools/cargo/tests/testsuite/profile_targets.rs b/src/tools/cargo/tests/testsuite/profile_targets.rs
index f2de169b9..9f00b73f3 100644
--- a/src/tools/cargo/tests/testsuite/profile_targets.rs
+++ b/src/tools/cargo/tests/testsuite/profile_targets.rs
@@ -667,5 +667,6 @@ fn profile_selection_doc() {
[DOCUMENTING] foo [..]
[RUNNING] `rustdoc [..]--crate-name foo src/lib.rs [..]
[FINISHED] dev [unoptimized + debuginfo] [..]
+[GENERATED] [CWD]/target/doc/foo/index.html
").run();
}
diff --git a/src/tools/cargo/tests/testsuite/profile_trim_paths.rs b/src/tools/cargo/tests/testsuite/profile_trim_paths.rs
new file mode 100644
index 000000000..1d24c159b
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/profile_trim_paths.rs
@@ -0,0 +1,614 @@
+//! Tests for `-Ztrim-paths`.
+
+use cargo_test_support::basic_manifest;
+use cargo_test_support::git;
+use cargo_test_support::paths;
+use cargo_test_support::project;
+use cargo_test_support::registry::Package;
+
+#[cargo_test]
+fn gated_manifest() {
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+
+ [profile.dev]
+ trim-paths = "macro"
+ "#,
+ )
+ .file("src/lib.rs", "")
+ .build();
+
+ p.cargo("check")
+ .masquerade_as_nightly_cargo(&["-Ztrim-paths"])
+ .with_status(101)
+ .with_stderr_contains(
+ "\
+[ERROR] failed to parse manifest at `[CWD]/Cargo.toml`
+
+Caused by:
+ feature `trim-paths` is required",
+ )
+ .run();
+}
+
+#[cargo_test]
+fn gated_config_toml() {
+ let p = project()
+ .file(
+ ".cargo/config.toml",
+ r#"
+ [profile.dev]
+ trim-paths = "macro"
+ "#,
+ )
+ .file("src/lib.rs", "")
+ .build();
+
+ p.cargo("check")
+ .masquerade_as_nightly_cargo(&["-Ztrim-paths"])
+ .with_status(101)
+ .with_stderr_contains(
+ "\
+[ERROR] config profile `dev` is not valid (defined in `[CWD]/.cargo/config.toml`)
+
+Caused by:
+ feature `trim-paths` is required",
+ )
+ .run();
+}
+
+#[cargo_test(nightly, reason = "-Zremap-path-scope is unstable")]
+fn release_profile_default_to_object() {
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+ "#,
+ )
+ .file("src/lib.rs", "")
+ .build();
+
+ p.cargo("build --release --verbose -Ztrim-paths")
+ .masquerade_as_nightly_cargo(&["-Ztrim-paths"])
+ .with_stderr(
+ "\
+[COMPILING] foo v0.0.1 ([CWD])
+[RUNNING] `rustc [..]\
+ -Zremap-path-scope=object \
+ --remap-path-prefix=[..]/lib/rustlib/src/rust=/rustc/[..] \
+ --remap-path-prefix=[CWD]= [..]
+[FINISHED] release [..]",
+ )
+ .run();
+}
+
+#[cargo_test(nightly, reason = "-Zremap-path-scope is unstable")]
+fn one_option() {
+ let build = |option| {
+ let p = project()
+ .file(
+ "Cargo.toml",
+ &format!(
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+
+ [profile.dev]
+ trim-paths = "{option}"
+ "#
+ ),
+ )
+ .file("src/lib.rs", "")
+ .build();
+
+ p.cargo("build -v -Ztrim-paths")
+ };
+
+ for option in ["macro", "diagnostics", "object", "all"] {
+ build(option)
+ .masquerade_as_nightly_cargo(&["-Ztrim-paths"])
+ .with_stderr(&format!(
+ "\
+[COMPILING] foo v0.0.1 ([CWD])
+[RUNNING] `rustc [..]\
+ -Zremap-path-scope={option} \
+ --remap-path-prefix=[..]/lib/rustlib/src/rust=/rustc/[..] \
+ --remap-path-prefix=[CWD]= [..]
+[FINISHED] dev [..]",
+ ))
+ .run();
+ }
+ build("none")
+ .masquerade_as_nightly_cargo(&["-Ztrim-paths"])
+ .with_stderr_does_not_contain("[..]-Zremap-path-scope=[..]")
+ .with_stderr_does_not_contain("[..]--remap-path-prefix=[..]")
+ .run();
+}
+
+#[cargo_test(nightly, reason = "-Zremap-path-scope is unstable")]
+fn multiple_options() {
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+
+ [profile.dev]
+ trim-paths = ["diagnostics", "macro", "object"]
+ "#,
+ )
+ .file("src/lib.rs", "")
+ .build();
+
+ p.cargo("build --verbose -Ztrim-paths")
+ .masquerade_as_nightly_cargo(&["-Ztrim-paths"])
+ .with_stderr(
+ "\
+[COMPILING] foo v0.0.1 ([CWD])
+[RUNNING] `rustc [..]\
+ -Zremap-path-scope=diagnostics,macro,object \
+ --remap-path-prefix=[..]/lib/rustlib/src/rust=/rustc/[..] \
+ --remap-path-prefix=[CWD]= [..]
+[FINISHED] dev [..]",
+ )
+ .run();
+}
+
+#[cargo_test(nightly, reason = "-Zremap-path-scope is unstable")]
+fn profile_merge_works() {
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+
+ [profile.dev]
+ trim-paths = ["macro"]
+
+ [profile.custom]
+ inherits = "dev"
+ trim-paths = ["diagnostics"]
+ "#,
+ )
+ .file("src/lib.rs", "")
+ .build();
+
+ p.cargo("build -v -Ztrim-paths --profile custom")
+ .masquerade_as_nightly_cargo(&["-Ztrim-paths"])
+ .with_stderr(
+ "\
+[COMPILING] foo v0.0.1 ([CWD])
+[RUNNING] `rustc [..]\
+ -Zremap-path-scope=diagnostics \
+ --remap-path-prefix=[..]/lib/rustlib/src/rust=/rustc/[..] \
+ --remap-path-prefix=[CWD]= [..]
+[FINISHED] custom [..]",
+ )
+ .run();
+}
+
+#[cargo_test(nightly, reason = "-Zremap-path-scope is unstable")]
+fn registry_dependency() {
+ Package::new("bar", "0.0.1")
+ .file("Cargo.toml", &basic_manifest("bar", "0.0.1"))
+ .file("src/lib.rs", r#"pub fn f() { println!("{}", file!()); }"#)
+ .publish();
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+
+ [dependencies]
+ bar = "0.0.1"
+
+ [profile.dev]
+ trim-paths = "object"
+ "#,
+ )
+ .file("src/main.rs", "fn main() { bar::f(); }")
+ .build();
+
+ let registry_src = paths::home().join(".cargo/registry/src");
+ let pkg_remap = format!("{}/[..]/bar-0.0.1=bar-0.0.1", registry_src.display());
+
+ p.cargo("run --verbose -Ztrim-paths")
+ .masquerade_as_nightly_cargo(&["-Ztrim-paths"])
+ .with_stdout("bar-0.0.1/src/lib.rs")
+ .with_stderr(&format!(
+ "\
+[UPDATING] [..]
+[DOWNLOADING] crates ...
+[DOWNLOADED] bar v0.0.1 ([..])
+[COMPILING] bar v0.0.1
+[RUNNING] `rustc [..]\
+ -Zremap-path-scope=object \
+ --remap-path-prefix=[..]/lib/rustlib/src/rust=/rustc/[..] \
+ --remap-path-prefix={pkg_remap} [..]
+[COMPILING] foo v0.0.1 ([CWD])
+[RUNNING] `rustc [..]\
+ -Zremap-path-scope=object \
+ --remap-path-prefix=[..]/lib/rustlib/src/rust=/rustc/[..] \
+ --remap-path-prefix=[CWD]= [..]
+[FINISHED] dev [..]
+[RUNNING] `target/debug/foo[EXE]`"
+ ))
+ .run();
+}
+
+#[cargo_test(nightly, reason = "-Zremap-path-scope is unstable")]
+fn git_dependency() {
+ let git_project = git::new("bar", |project| {
+ project
+ .file("Cargo.toml", &basic_manifest("bar", "0.0.1"))
+ .file("src/lib.rs", r#"pub fn f() { println!("{}", file!()); }"#)
+ });
+ let url = git_project.url();
+
+ let p = project()
+ .file(
+ "Cargo.toml",
+ &format!(
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+
+ [dependencies]
+ bar = {{ git = "{url}" }}
+
+ [profile.dev]
+ trim-paths = "object"
+ "#
+ ),
+ )
+ .file("src/main.rs", "fn main() { bar::f(); }")
+ .build();
+
+ let git_checkouts_src = paths::home().join(".cargo/git/checkouts");
+ let pkg_remap = format!("{}/bar-[..]/[..]=bar-0.0.1", git_checkouts_src.display());
+
+ p.cargo("run --verbose -Ztrim-paths")
+ .masquerade_as_nightly_cargo(&["-Ztrim-paths"])
+ .with_stdout("bar-0.0.1/src/lib.rs")
+ .with_stderr(&format!(
+ "\
+[UPDATING] git repository `{url}`
+[COMPILING] bar v0.0.1 ({url}[..])
+[RUNNING] `rustc [..]\
+ -Zremap-path-scope=object \
+ --remap-path-prefix=[..]/lib/rustlib/src/rust=/rustc/[..] \
+ --remap-path-prefix={pkg_remap} [..]
+[COMPILING] foo v0.0.1 ([CWD])
+[RUNNING] `rustc [..]\
+ -Zremap-path-scope=object \
+ --remap-path-prefix=[..]/lib/rustlib/src/rust=/rustc/[..] \
+ --remap-path-prefix=[CWD]= [..]
+[FINISHED] dev [..]
+[RUNNING] `target/debug/foo[EXE]`"
+ ))
+ .run();
+}
+
+#[cargo_test(nightly, reason = "-Zremap-path-scope is unstable")]
+fn path_dependency() {
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+
+ [dependencies]
+ bar = { path = "cocktail-bar" }
+
+ [profile.dev]
+ trim-paths = "object"
+ "#,
+ )
+ .file("src/main.rs", "fn main() { bar::f(); }")
+ .file("cocktail-bar/Cargo.toml", &basic_manifest("bar", "0.0.1"))
+ .file(
+ "cocktail-bar/src/lib.rs",
+ r#"pub fn f() { println!("{}", file!()); }"#,
+ )
+ .build();
+
+ p.cargo("run --verbose -Ztrim-paths")
+ .masquerade_as_nightly_cargo(&["-Ztrim-paths"])
+ .with_stdout("cocktail-bar/src/lib.rs")
+ .with_stderr(&format!(
+ "\
+[COMPILING] bar v0.0.1 ([..]/cocktail-bar)
+[RUNNING] `rustc [..]\
+ -Zremap-path-scope=object \
+ --remap-path-prefix=[..]/lib/rustlib/src/rust=/rustc/[..] \
+ --remap-path-prefix=[CWD]= [..]
+[COMPILING] foo v0.0.1 ([CWD])
+[RUNNING] `rustc [..]\
+ -Zremap-path-scope=object \
+ --remap-path-prefix=[..]/lib/rustlib/src/rust=/rustc/[..] \
+ --remap-path-prefix=[CWD]= [..]
+[FINISHED] dev [..]
+[RUNNING] `target/debug/foo[EXE]`"
+ ))
+ .run();
+}
+
+#[cargo_test(nightly, reason = "-Zremap-path-scope is unstable")]
+fn path_dependency_outside_workspace() {
+ let bar = project()
+ .at("bar")
+ .file("Cargo.toml", &basic_manifest("bar", "0.0.1"))
+ .file("src/lib.rs", r#"pub fn f() { println!("{}", file!()); }"#)
+ .build();
+ let bar_path = bar.url().to_file_path().unwrap();
+ let bar_path = bar_path.display();
+
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+
+ [dependencies]
+ bar = { path = "../bar" }
+
+ [profile.dev]
+ trim-paths = "object"
+ "#,
+ )
+ .file("src/main.rs", "fn main() { bar::f(); }")
+ .build();
+
+ p.cargo("run --verbose -Ztrim-paths")
+ .masquerade_as_nightly_cargo(&["-Ztrim-paths"])
+ .with_stdout("bar-0.0.1/src/lib.rs")
+ .with_stderr(&format!(
+ "\
+[COMPILING] bar v0.0.1 ([..]/bar)
+[RUNNING] `rustc [..]\
+ -Zremap-path-scope=object \
+ --remap-path-prefix=[..]/lib/rustlib/src/rust=/rustc/[..] \
+ --remap-path-prefix={bar_path}=bar-0.0.1 [..]
+[COMPILING] foo v0.0.1 ([CWD])
+[RUNNING] `rustc [..]\
+ -Zremap-path-scope=object \
+ --remap-path-prefix=[..]/lib/rustlib/src/rust=/rustc/[..] \
+ --remap-path-prefix=[CWD]= [..]
+[FINISHED] dev [..]
+[RUNNING] `target/debug/foo[EXE]`"
+ ))
+ .run();
+}
+
+#[cargo_test(nightly, reason = "-Zremap-path-scope is unstable")]
+fn diagnostics_works() {
+ Package::new("bar", "0.0.1")
+ .file("Cargo.toml", &basic_manifest("bar", "0.0.1"))
+ .file("src/lib.rs", r#"pub fn f() { let unused = 0; }"#)
+ .publish();
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+
+ [dependencies]
+ bar = "0.0.1"
+
+ [profile.dev]
+ trim-paths = "diagnostics"
+ "#,
+ )
+ .file("src/lib.rs", "")
+ .build();
+
+ let registry_src = paths::home().join(".cargo/registry/src");
+ let registry_src = registry_src.display();
+ let pkg_remap = format!("{registry_src}/[..]/bar-0.0.1=bar-0.0.1");
+
+ p.cargo("build -vv -Ztrim-paths")
+ .masquerade_as_nightly_cargo(&["-Ztrim-paths"])
+ .with_stderr_line_without(
+ &["[..]bar-0.0.1/src/lib.rs:1[..]"],
+ &[&format!("{registry_src}")],
+ )
+ .with_stderr_contains("[..]unused_variables[..]")
+ .with_stderr_contains(&format!(
+ "\
+[RUNNING] [..]rustc [..]\
+ -Zremap-path-scope=diagnostics \
+ --remap-path-prefix=[..]/lib/rustlib/src/rust=/rustc/[..] \
+ --remap-path-prefix={pkg_remap} [..]",
+ ))
+ .with_stderr_contains(
+ "\
+[RUNNING] [..]rustc [..]\
+ -Zremap-path-scope=diagnostics \
+ --remap-path-prefix=[..]/lib/rustlib/src/rust=/rustc/[..] \
+ --remap-path-prefix=[CWD]= [..]",
+ )
+ .run();
+}
+
+#[cfg(target_os = "linux")]
+#[cargo_test(requires_readelf, nightly, reason = "-Zremap-path-scope is unstable")]
+fn object_works() {
+ use std::os::unix::ffi::OsStrExt;
+
+ let run_readelf = |path| {
+ std::process::Command::new("readelf")
+ .arg("-wi")
+ .arg(path)
+ .output()
+ .expect("readelf works")
+ };
+
+ let registry_src = paths::home().join(".cargo/registry/src");
+ let pkg_remap = format!("{}/[..]/bar-0.0.1=bar-0.0.1", registry_src.display());
+ let rust_src = "/lib/rustc/src/rust".as_bytes();
+ let registry_src_bytes = registry_src.as_os_str().as_bytes();
+
+ Package::new("bar", "0.0.1")
+ .file("Cargo.toml", &basic_manifest("bar", "0.0.1"))
+ .file("src/lib.rs", r#"pub fn f() { println!("{}", file!()); }"#)
+ .publish();
+
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+
+ [dependencies]
+ bar = "0.0.1"
+ "#,
+ )
+ .file("src/main.rs", "fn main() { bar::f(); }")
+ .build();
+
+ let pkg_root = p.root();
+ let pkg_root = pkg_root.as_os_str().as_bytes();
+
+ p.cargo("build").run();
+
+ let bin_path = p.bin("foo");
+ assert!(bin_path.is_file());
+ let stdout = run_readelf(bin_path).stdout;
+ // TODO: re-enable this check when rustc bootstrap disables remapping
+ // <https://github.com/rust-lang/cargo/pull/12625#discussion_r1371714791>
+ // assert!(memchr::memmem::find(&stdout, rust_src).is_some());
+ assert!(memchr::memmem::find(&stdout, registry_src_bytes).is_some());
+ assert!(memchr::memmem::find(&stdout, pkg_root).is_some());
+
+ p.cargo("clean").run();
+
+ p.change_file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+
+ [dependencies]
+ bar = "0.0.1"
+
+ [profile.dev]
+ trim-paths = "object"
+ "#,
+ );
+
+ p.cargo("build --verbose -Ztrim-paths")
+ .masquerade_as_nightly_cargo(&["-Ztrim-paths"])
+ .with_stderr(&format!(
+ "\
+[COMPILING] bar v0.0.1
+[RUNNING] `rustc [..]\
+ -Zremap-path-scope=object \
+ --remap-path-prefix=[..]/lib/rustlib/src/rust=/rustc/[..] \
+ --remap-path-prefix={pkg_remap} [..]
+[COMPILING] foo v0.0.1 ([CWD])
+[RUNNING] `rustc [..]\
+ -Zremap-path-scope=object \
+ --remap-path-prefix=[..]/lib/rustlib/src/rust=/rustc/[..] \
+ --remap-path-prefix=[CWD]= [..]
+[FINISHED] dev [..]",
+ ))
+ .run();
+
+ let bin_path = p.bin("foo");
+ assert!(bin_path.is_file());
+ let stdout = run_readelf(bin_path).stdout;
+ assert!(memchr::memmem::find(&stdout, rust_src).is_none());
+ assert!(memchr::memmem::find(&stdout, registry_src_bytes).is_none());
+ assert!(memchr::memmem::find(&stdout, pkg_root).is_none());
+}
+
+// TODO: might want to move to test/testsuite/build_script.rs once stabilized.
+#[cargo_test(nightly, reason = "-Zremap-path-scope is unstable")]
+fn custom_build_env_var_trim_paths() {
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+ "#,
+ )
+ .file("src/lib.rs", "")
+ .file("build.rs", "")
+ .build();
+
+ let test_cases = [
+ ("[]", "none"),
+ ("\"all\"", "all"),
+ ("\"diagnostics\"", "diagnostics"),
+ ("\"macro\"", "macro"),
+ ("\"none\"", "none"),
+ ("\"object\"", "object"),
+ ("false", "none"),
+ ("true", "all"),
+ (
+ r#"["diagnostics", "macro", "object"]"#,
+ "diagnostics,macro,object",
+ ),
+ ];
+
+ for (opts, expected) in test_cases {
+ p.change_file(
+ "Cargo.toml",
+ &format!(
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+
+ [profile.dev]
+ trim-paths = {opts}
+ "#
+ ),
+ );
+
+ p.change_file(
+ "build.rs",
+ &format!(
+ r#"
+ fn main() {{
+ assert_eq!(
+ std::env::var("CARGO_TRIM_PATHS").unwrap().as_str(),
+ "{expected}",
+ );
+ }}
+ "#
+ ),
+ );
+
+ p.cargo("build -Ztrim-paths")
+ .masquerade_as_nightly_cargo(&["-Ztrim-paths"])
+ .run();
+ }
+}
diff --git a/src/tools/cargo/tests/testsuite/pub_priv.rs b/src/tools/cargo/tests/testsuite/pub_priv.rs
index 83c6a49f8..b2160e0fa 100644
--- a/src/tools/cargo/tests/testsuite/pub_priv.rs
+++ b/src/tools/cargo/tests/testsuite/pub_priv.rs
@@ -197,3 +197,52 @@ Caused by:
)
.run()
}
+
+#[cargo_test(nightly, reason = "exported_private_dependencies lint is unstable")]
+fn workspace_dep_made_public() {
+ Package::new("foo1", "0.1.0")
+ .file("src/lib.rs", "pub struct FromFoo;")
+ .publish();
+ Package::new("foo2", "0.1.0")
+ .file("src/lib.rs", "pub struct FromFoo;")
+ .publish();
+ Package::new("foo3", "0.1.0")
+ .file("src/lib.rs", "pub struct FromFoo;")
+ .publish();
+
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ cargo-features = ["public-dependency"]
+
+ [package]
+ name = "foo"
+ version = "0.0.1"
+
+ [workspace.dependencies]
+ foo1 = "0.1.0"
+ foo2 = { version = "0.1.0", public = true }
+ foo3 = { version = "0.1.0", public = false }
+
+ [dependencies]
+ foo1 = { workspace = true, public = true }
+ foo2 = { workspace = true }
+ foo3 = { workspace = true, public = true }
+ "#,
+ )
+ .file(
+ "src/lib.rs",
+ "
+ #![deny(exported_private_dependencies)]
+ pub fn use_priv1(_: foo1::FromFoo) {}
+ pub fn use_priv2(_: foo2::FromFoo) {}
+ pub fn use_priv3(_: foo3::FromFoo) {}
+ ",
+ )
+ .build();
+
+ p.cargo("check")
+ .masquerade_as_nightly_cargo(&["public-dependency"])
+ .run()
+}
diff --git a/src/tools/cargo/tests/testsuite/publish.rs b/src/tools/cargo/tests/testsuite/publish.rs
index 67569bf3b..5d29ac88a 100644
--- a/src/tools/cargo/tests/testsuite/publish.rs
+++ b/src/tools/cargo/tests/testsuite/publish.rs
@@ -420,7 +420,7 @@ fn unpublishable_crate() {
.with_stderr(
"\
[ERROR] `foo` cannot be published.
-`package.publish` is set to `false` or an empty list in Cargo.toml and prevents publishing.
+`package.publish` must be set to `true` or a non-empty list in Cargo.toml to publish.
",
)
.run();
@@ -794,7 +794,7 @@ fn publish_empty_list() {
.with_stderr(
"\
[ERROR] `foo` cannot be published.
-`package.publish` is set to `false` or an empty list in Cargo.toml and prevents publishing.
+`package.publish` must be set to `true` or a non-empty list in Cargo.toml to publish.
",
)
.run();
@@ -1020,7 +1020,7 @@ fn block_publish_no_registry() {
.with_stderr(
"\
[ERROR] `foo` cannot be published.
-`package.publish` is set to `false` or an empty list in Cargo.toml and prevents publishing.
+`package.publish` must be set to `true` or a non-empty list in Cargo.toml to publish.
",
)
.run();
@@ -3004,3 +3004,32 @@ Caused by:
.with_status(101)
.run();
}
+
+#[cargo_test]
+fn versionless_package() {
+ // Use local registry for faster test times since no publish will occur
+ let registry = registry::init();
+
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ description = "foo"
+ "#,
+ )
+ .file("src/main.rs", r#"fn main() { println!("hello"); }"#)
+ .build();
+
+ p.cargo("publish")
+ .replace_crates_io(registry.index_url())
+ .with_status(101)
+ .with_stderr(
+ "\
+error: `foo` cannot be published.
+`package.publish` must be set to `true` or a non-empty list in Cargo.toml to publish.
+",
+ )
+ .run();
+}
diff --git a/src/tools/cargo/tests/testsuite/registry.rs b/src/tools/cargo/tests/testsuite/registry.rs
index f485180c9..b5dff2746 100644
--- a/src/tools/cargo/tests/testsuite/registry.rs
+++ b/src/tools/cargo/tests/testsuite/registry.rs
@@ -3600,4 +3600,55 @@ fn differ_only_by_metadata() {
",
)
.run();
+
+ Package::new("baz", "0.0.1+d").publish();
+
+ p.cargo("clean").run();
+ p.cargo("check")
+ .with_stderr_contains("[CHECKING] baz v0.0.1+b")
+ .run();
+}
+
+#[cargo_test]
+fn differ_only_by_metadata_with_lockfile() {
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+ authors = []
+
+ [dependencies]
+ baz = "=0.0.1"
+ "#,
+ )
+ .file("src/main.rs", "fn main() {}")
+ .build();
+
+ Package::new("baz", "0.0.1+a").publish();
+ Package::new("baz", "0.0.1+b").publish();
+ Package::new("baz", "0.0.1+c").publish();
+
+ p.cargo("update --package baz --precise 0.0.1+b")
+ .with_stderr(
+ "\
+[UPDATING] [..] index
+[..] baz v0.0.1+c -> v0.0.1+b
+",
+ )
+ .run();
+
+ p.cargo("check")
+ .with_stderr(
+ "\
+[DOWNLOADING] crates ...
+[DOWNLOADED] [..] v0.0.1+b (registry `dummy-registry`)
+[CHECKING] baz v0.0.1+b
+[CHECKING] foo v0.0.1 ([CWD])
+[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]s
+",
+ )
+ .run();
}
diff --git a/src/tools/cargo/tests/testsuite/run.rs b/src/tools/cargo/tests/testsuite/run.rs
index c58c239ac..c7ddd5d9e 100644
--- a/src/tools/cargo/tests/testsuite/run.rs
+++ b/src/tools/cargo/tests/testsuite/run.rs
@@ -50,7 +50,7 @@ error: unexpected argument '--silent' found
tip: a similar argument exists: '--quiet'
-Usage: cargo[EXE] run [OPTIONS] [args]...
+Usage: cargo[EXE] run [OPTIONS] [ARGS]...
For more information, try '--help'.
",
@@ -65,7 +65,7 @@ error: unexpected argument '--silent' found
tip: a similar argument exists: '--quiet'
-Usage: cargo[EXE] run [OPTIONS] [args]...
+Usage: cargo[EXE] run [OPTIONS] [ARGS]...
For more information, try '--help'.
",
diff --git a/src/tools/cargo/tests/testsuite/rustdoc.rs b/src/tools/cargo/tests/testsuite/rustdoc.rs
index 5650f3e0a..7ef768a80 100644
--- a/src/tools/cargo/tests/testsuite/rustdoc.rs
+++ b/src/tools/cargo/tests/testsuite/rustdoc.rs
@@ -15,6 +15,7 @@ fn rustdoc_simple() {
[..] \
-L dependency=[CWD]/target/debug/deps [..]`
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+[GENERATED] [CWD]/target/doc/foo/index.html
",
)
.run();
@@ -35,6 +36,7 @@ fn rustdoc_args() {
-C metadata=[..] \
-L dependency=[CWD]/target/debug/deps [..]`
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+[GENERATED] [CWD]/target/doc/foo/index.html
",
)
.run();
@@ -88,6 +90,7 @@ fn rustdoc_foo_with_bar_dependency() {
-L dependency=[CWD]/target/debug/deps \
--extern [..]`
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+[GENERATED] [CWD]/target/doc/foo/index.html
",
)
.run();
@@ -127,6 +130,7 @@ fn rustdoc_only_bar_dependency() {
-C metadata=[..] \
-L dependency=[CWD]/target/debug/deps [..]`
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+[GENERATED] [CWD]/target/doc/bar/index.html
",
)
.run();
@@ -150,6 +154,7 @@ fn rustdoc_same_name_documents_lib() {
-C metadata=[..] \
-L dependency=[CWD]/target/debug/deps [..]`
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+[GENERATED] [CWD]/target/doc/foo/index.html
",
)
.run();
@@ -225,7 +230,8 @@ fn rustdoc_target() {
[..] \
-L dependency=[CWD]/target/{target}/debug/deps \
-L dependency=[CWD]/target/debug/deps[..]`
-[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]",
+[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+[GENERATED] [CWD]/target/[..]/doc/foo/index.html",
target = cross_compile::alternate()
))
.run();
diff --git a/src/tools/cargo/tests/testsuite/rustdocflags.rs b/src/tools/cargo/tests/testsuite/rustdocflags.rs
index c37d5a826..e7c2aa263 100644
--- a/src/tools/cargo/tests/testsuite/rustdocflags.rs
+++ b/src/tools/cargo/tests/testsuite/rustdocflags.rs
@@ -48,7 +48,10 @@ fn rerun() {
p.cargo("doc").env("RUSTDOCFLAGS", "--cfg=foo").run();
p.cargo("doc")
.env("RUSTDOCFLAGS", "--cfg=foo")
- .with_stderr("[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]")
+ .with_stderr(
+ "[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+[GENERATED] [CWD]/target/doc/foo/index.html",
+ )
.run();
p.cargo("doc")
.env("RUSTDOCFLAGS", "--cfg=bar")
@@ -56,6 +59,7 @@ fn rerun() {
"\
[DOCUMENTING] foo v0.0.1 ([..])
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+[GENERATED] [CWD]/target/doc/foo/index.html
",
)
.run();
diff --git a/src/tools/cargo/tests/testsuite/script.rs b/src/tools/cargo/tests/testsuite/script.rs
index 96f3a5eb4..0b1e5a6b9 100644
--- a/src/tools/cargo/tests/testsuite/script.rs
+++ b/src/tools/cargo/tests/testsuite/script.rs
@@ -108,6 +108,7 @@ error: no such command: `echo`
<tab>Did you mean `bench`?
<tab>View all installed commands with `cargo --list`
+<tab>Find a package to install `echo` with `cargo search cargo-echo`
",
)
.run();
diff --git a/src/tools/cargo/tests/testsuite/search.rs b/src/tools/cargo/tests/testsuite/search.rs
index 4c3155c8f..c76397ac7 100644
--- a/src/tools/cargo/tests/testsuite/search.rs
+++ b/src/tools/cargo/tests/testsuite/search.rs
@@ -1,5 +1,6 @@
//! Tests for the `cargo search` command.
+use cargo::util::cache_lock::CacheLockMode;
use cargo_test_support::cargo_process;
use cargo_test_support::paths;
use cargo_test_support::registry::{RegistryBuilder, Response};
@@ -100,7 +101,9 @@ fn not_update() {
paths::root(),
paths::home().join(".cargo"),
);
- let lock = cfg.acquire_package_cache_lock().unwrap();
+ let lock = cfg
+ .acquire_package_cache_lock(CacheLockMode::DownloadExclusive)
+ .unwrap();
let mut regsrc = RegistrySource::remote(sid, &HashSet::new(), &cfg).unwrap();
regsrc.invalidate_cache();
regsrc.block_until_ready().unwrap();
diff --git a/src/tools/cargo/tests/testsuite/update.rs b/src/tools/cargo/tests/testsuite/update.rs
index fe1d86bd7..e636435b0 100644
--- a/src/tools/cargo/tests/testsuite/update.rs
+++ b/src/tools/cargo/tests/testsuite/update.rs
@@ -392,6 +392,104 @@ fn update_precise() {
}
#[cargo_test]
+fn update_precise_mismatched() {
+ Package::new("serde", "1.2.0").publish();
+ Package::new("serde", "1.2.1").publish();
+ Package::new("serde", "1.6.0").publish();
+
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "bar"
+ version = "0.0.1"
+ authors = []
+
+ [dependencies]
+ serde = "~1.2"
+ "#,
+ )
+ .file("src/lib.rs", "")
+ .build();
+
+ p.cargo("check").run();
+
+ // `1.6.0` does not match `"~1.2"`
+ p.cargo("update serde:1.2 --precise 1.6.0")
+ .with_stderr(
+ "\
+[UPDATING] `[..]` index
+[ERROR] failed to select a version for the requirement `serde = \"~1.2\"`
+candidate versions found which didn't match: 1.6.0
+location searched: `[..]` index (which is replacing registry `crates-io`)
+required by package `bar v0.0.1 ([..]/foo)`
+perhaps a crate was updated and forgotten to be re-vendored?
+",
+ )
+ .with_status(101)
+ .run();
+
+ // `1.9.0` does not exist
+ p.cargo("update serde:1.2 --precise 1.9.0")
+ // This terrible error message has been the same for a long time. A fix is more than welcome!
+ .with_stderr(
+ "\
+[UPDATING] `[..]` index
+[ERROR] no matching package named `serde` found
+location searched: registry `crates-io`
+required by package `bar v0.0.1 ([..]/foo)`
+",
+ )
+ .with_status(101)
+ .run();
+}
+
+#[cargo_test]
+fn update_precise_build_metadata() {
+ Package::new("serde", "0.0.1+first").publish();
+ Package::new("serde", "0.0.1+second").publish();
+
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.0"
+
+ [dependencies]
+ serde = "0.0.1"
+ "#,
+ )
+ .file("src/lib.rs", "")
+ .build();
+
+ p.cargo("generate-lockfile").run();
+ p.cargo("update serde --precise 0.0.1+first").run();
+
+ p.cargo("update serde --precise 0.0.1+second")
+ .with_stderr(
+ "\
+[UPDATING] `[..]` index
+[UPDATING] serde v0.0.1+first -> v0.0.1+second
+",
+ )
+ .run();
+
+ // This is not considered "Downgrading". Build metadata are not assumed to
+ // be ordered.
+ p.cargo("update serde --precise 0.0.1+first")
+ .with_stderr(
+ "\
+[UPDATING] `[..]` index
+[UPDATING] serde v0.0.1+second -> v0.0.1+first
+",
+ )
+ .run();
+}
+
+#[cargo_test]
fn update_precise_do_not_force_update_deps() {
Package::new("log", "0.1.0").publish();
Package::new("serde", "0.2.1").dep("log", "0.1").publish();
diff --git a/src/tools/cargo/tests/testsuite/version.rs b/src/tools/cargo/tests/testsuite/version.rs
index f880c75a6..110e61003 100644
--- a/src/tools/cargo/tests/testsuite/version.rs
+++ b/src/tools/cargo/tests/testsuite/version.rs
@@ -13,6 +13,10 @@ fn simple() {
p.cargo("--version")
.with_stdout(&format!("cargo {}\n", cargo::version()))
.run();
+
+ p.cargo("-V")
+ .with_stdout(&format!("cargo {}\n", cargo::version()))
+ .run();
}
#[cargo_test]
diff --git a/src/tools/cargo/tests/testsuite/warn_on_failure.rs b/src/tools/cargo/tests/testsuite/warn_on_failure.rs
index 19cb01813..f2c2bb071 100644
--- a/src/tools/cargo/tests/testsuite/warn_on_failure.rs
+++ b/src/tools/cargo/tests/testsuite/warn_on_failure.rs
@@ -105,7 +105,7 @@ fn warning_on_lib_failure() {
.with_stderr_contains("[UPDATING] `[..]` index")
.with_stderr_contains("[DOWNLOADED] bar v0.0.1 ([..])")
.with_stderr_contains("[COMPILING] bar v0.0.1")
- .with_stderr_contains(&format!("[WARNING] {}", WARNING1))
- .with_stderr_contains(&format!("[WARNING] {}", WARNING2))
+ .with_stderr_contains(&format!("[WARNING] bar@0.0.1: {}", WARNING1))
+ .with_stderr_contains(&format!("[WARNING] bar@0.0.1: {}", WARNING2))
.run();
}
diff --git a/src/tools/cargo/tests/testsuite/workspaces.rs b/src/tools/cargo/tests/testsuite/workspaces.rs
index 4f8997b38..94b5142f4 100644
--- a/src/tools/cargo/tests/testsuite/workspaces.rs
+++ b/src/tools/cargo/tests/testsuite/workspaces.rs
@@ -1046,7 +1046,7 @@ fn members_include_path_deps() {
}
#[cargo_test]
-fn new_warns_you_this_will_not_work() {
+fn new_creates_members_list() {
let p = project()
.file(
"Cargo.toml",
@@ -1063,20 +1063,7 @@ fn new_warns_you_this_will_not_work() {
let p = p.build();
p.cargo("new --lib bar")
- .with_stderr(
- "\
-warning: compiling this new package may not work due to invalid workspace configuration
-
-current package believes it's in a workspace when it's not:
-current: [..]
-workspace: [..]
-
-this may be fixable by ensuring that this crate is depended on by the workspace \
-root: [..]
-[..]
-[CREATED] library `bar` package
-",
- )
+ .with_stderr(" Created library `bar` package")
.run();
}
diff --git a/src/tools/cargo/triagebot.toml b/src/tools/cargo/triagebot.toml
index c92b4ce8c..cdf1090a1 100644
--- a/src/tools/cargo/triagebot.toml
+++ b/src/tools/cargo/triagebot.toml
@@ -36,6 +36,15 @@ warn_non_default_branch = true
[assign.owners]
"*" = ["@ehuss", "@epage", "@weihanglo"]
+
+[review-submitted]
+reviewed_label = "S-waiting-on-author"
+review_labels = ["S-waiting-on-review"]
+
+[review-requested]
+remove_labels = ["S-waiting-on-author"]
+add_labels = ["S-waiting-on-review"]
+
[autolabel."A-build-execution"]
trigger_files = [
"src/cargo/core/compiler/compilation.rs",
@@ -192,7 +201,6 @@ trigger_files = ["src/cargo/util/auth/"]
trigger_files = [
"crates/semver-check",
"src/cargo/util/semver_ext.rs",
- "src/cargo/util/to_semver.rs",
]
[autolabel."A-source-replacement"]
diff --git a/src/tools/clippy/.github/workflows/clippy.yml b/src/tools/clippy/.github/workflows/clippy.yml
index 410ff53a2..99d80bec0 100644
--- a/src/tools/clippy/.github/workflows/clippy.yml
+++ b/src/tools/clippy/.github/workflows/clippy.yml
@@ -60,7 +60,7 @@ jobs:
working-directory: clippy_lints
- name: Test clippy_utils
- run: cargo test --features deny-warnings,internal
+ run: cargo test --features deny-warnings
working-directory: clippy_utils
- name: Test rustc_tools_util
diff --git a/src/tools/clippy/.github/workflows/clippy_bors.yml b/src/tools/clippy/.github/workflows/clippy_bors.yml
index 9b96f8dc2..f67233dec 100644
--- a/src/tools/clippy/.github/workflows/clippy_bors.yml
+++ b/src/tools/clippy/.github/workflows/clippy_bors.yml
@@ -120,9 +120,13 @@ jobs:
working-directory: clippy_lints
- name: Test clippy_utils
- run: cargo test --features deny-warnings,internal
+ run: cargo test --features deny-warnings
working-directory: clippy_utils
+ - name: Test clippy_config
+ run: cargo test --features deny-warnings
+ working-directory: clippy_config
+
- name: Test rustc_tools_util
run: cargo test --features deny-warnings
working-directory: rustc_tools_util
diff --git a/src/tools/clippy/CHANGELOG.md b/src/tools/clippy/CHANGELOG.md
index 8c9ab1e24..87a96bdeb 100644
--- a/src/tools/clippy/CHANGELOG.md
+++ b/src/tools/clippy/CHANGELOG.md
@@ -6,11 +6,101 @@ document.
## Unreleased / Beta / In Rust Nightly
-[37f4c172...master](https://github.com/rust-lang/rust-clippy/compare/37f4c172...master)
+[1e8fdf49...master](https://github.com/rust-lang/rust-clippy/compare/1e8fdf49...master)
+
+## Rust 1.73
+
+Current stable, released 2023-10-05
+
+[View all 103 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2023-07-02T12%3A24%3A40Z..2023-08-11T11%3A09%3A56Z+base%3Amaster)
+
+### New Lints
+
+* [`impossible_comparisons`]
+ [#10843](https://github.com/rust-lang/rust-clippy/pull/10843)
+* [`redundant_comparisons`]
+ [#10843](https://github.com/rust-lang/rust-clippy/pull/10843)
+* [`ignored_unit_patterns`]
+ [#11242](https://github.com/rust-lang/rust-clippy/pull/11242)
+* [`readonly_write_lock`]
+ [#11210](https://github.com/rust-lang/rust-clippy/pull/11210)
+* [`filter_map_bool_then`]
+ [#11115](https://github.com/rust-lang/rust-clippy/pull/11115)
+* [`needless_return_with_question_mark`]
+ [#11031](https://github.com/rust-lang/rust-clippy/pull/11031)
+* [`redundant_guards`]
+ [#10955](https://github.com/rust-lang/rust-clippy/pull/10955)
+* [`redundant_locals`]
+ [#10885](https://github.com/rust-lang/rust-clippy/pull/10885)
+* [`absolute_paths`]
+ [#11003](https://github.com/rust-lang/rust-clippy/pull/11003)
+* [`error_impl_error`]
+ [#11107](https://github.com/rust-lang/rust-clippy/pull/11107)
+* [`iter_skip_zero`]
+ [#11046](https://github.com/rust-lang/rust-clippy/pull/11046)
+* [`string_lit_chars_any`]
+ [#11052](https://github.com/rust-lang/rust-clippy/pull/11052)
+* [`four_forward_slashes`]
+ [#11140](https://github.com/rust-lang/rust-clippy/pull/11140)
+* [`format_collect`]
+ [#11116](https://github.com/rust-lang/rust-clippy/pull/11116)
+* [`needless_pass_by_ref_mut`]
+ [#10900](https://github.com/rust-lang/rust-clippy/pull/10900)
+* [`manual_is_infinite`]
+ [#11049](https://github.com/rust-lang/rust-clippy/pull/11049)
+* [`manual_is_finite`]
+ [#11049](https://github.com/rust-lang/rust-clippy/pull/11049)
+* [`incorrect_partial_ord_impl_on_ord_type`]
+ [#10788](https://github.com/rust-lang/rust-clippy/pull/10788)
+* [`read_line_without_trim`]
+ [#10970](https://github.com/rust-lang/rust-clippy/pull/10970)
+* [`type_id_on_box`]
+ [#10987](https://github.com/rust-lang/rust-clippy/pull/10987)
+
+### Moves and Deprecations
+
+* Renamed `unwrap_or_else_default` to [`unwrap_or_default`]
+ [#10120](https://github.com/rust-lang/rust-clippy/pull/10120)
+* Moved [`tuple_array_conversions`] to `pedantic` (Now allow-by-default)
+ [#11146](https://github.com/rust-lang/rust-clippy/pull/11146)
+* Moved [`arc_with_non_send_sync`] to `suspicious` (Now warn-by-default)
+ [#11104](https://github.com/rust-lang/rust-clippy/pull/11104)
+* Moved [`needless_raw_string_hashes`] to `pedantic` (Now allow-by-default)
+ [#11415](https://github.com/rust-lang/rust-clippy/pull/11415)
+
+### Enhancements
+
+* [`unwrap_used`]: No longer lints on the never-type or never-like enums
+ [#11252](https://github.com/rust-lang/rust-clippy/pull/11252)
+* [`expect_used`]: No longer lints on the never-type or never-like enums
+ [#11252](https://github.com/rust-lang/rust-clippy/pull/11252)
+
+### False Positive Fixes
+
+* [`panic_in_result_fn`]: No longer triggers on `todo!`, `unimplemented!`, `unreachable!`
+ [#11123](https://github.com/rust-lang/rust-clippy/pull/11123)
+
+### Suggestion Fixes/Improvements
+
+* [`semicolon_if_nothing_returned`]: The suggestion is now machine-applicable with rustfix
+ [#11083](https://github.com/rust-lang/rust-clippy/pull/11083)
+
+### ICE Fixes
+
+* [`filter_map_bool_then`]: No longer crashes on late-bound regions
+ [#11318](https://github.com/rust-lang/rust-clippy/pull/11318)
+* [`unwrap_or_default`]: No longer crashes on alias types for local items
+ [#11258](https://github.com/rust-lang/rust-clippy/pull/11258)
+* [`unnecessary_literal_unwrap`]: No longer crashes on `None.unwrap_or_default()`
+ [#11106](https://github.com/rust-lang/rust-clippy/pull/11106)
+* Fixed MIR-related ICE
+ [#11130](https://github.com/rust-lang/rust-clippy/pull/11130)
+* [`missing_fields_in_debug`]: No longer crashes on non-ADT self types
+ [#11069](https://github.com/rust-lang/rust-clippy/pull/11069)
## Rust 1.72
-Current stable, released 2023-08-24
+Released 2023-08-24
[View all 131 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2023-05-22T14%3A53%3A59Z..2023-07-01T22%3A57%3A20Z+base%3Amaster)
@@ -5011,6 +5101,7 @@ Released 2018-09-13
[`integer_division`]: https://rust-lang.github.io/rust-clippy/master/index.html#integer_division
[`into_iter_on_array`]: https://rust-lang.github.io/rust-clippy/master/index.html#into_iter_on_array
[`into_iter_on_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#into_iter_on_ref
+[`into_iter_without_iter`]: https://rust-lang.github.io/rust-clippy/master/index.html#into_iter_without_iter
[`invalid_atomic_ordering`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_atomic_ordering
[`invalid_null_ptr_usage`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_null_ptr_usage
[`invalid_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_ref
@@ -5036,6 +5127,7 @@ Released 2018-09-13
[`iter_skip_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_skip_next
[`iter_skip_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_skip_zero
[`iter_with_drain`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_with_drain
+[`iter_without_into_iter`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_without_into_iter
[`iterator_step_by_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#iterator_step_by_zero
[`just_underscores_and_digits`]: https://rust-lang.github.io/rust-clippy/master/index.html#just_underscores_and_digits
[`large_const_arrays`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_const_arrays
@@ -5072,6 +5164,7 @@ Released 2018-09-13
[`manual_find`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_find
[`manual_find_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_find_map
[`manual_flatten`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_flatten
+[`manual_hash_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_hash_one
[`manual_instant_elapsed`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_instant_elapsed
[`manual_is_ascii_check`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_ascii_check
[`manual_is_finite`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_finite
@@ -5370,6 +5463,7 @@ Released 2018-09-13
[`string_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_to_string
[`strlen_on_c_strings`]: https://rust-lang.github.io/rust-clippy/master/index.html#strlen_on_c_strings
[`struct_excessive_bools`]: https://rust-lang.github.io/rust-clippy/master/index.html#struct_excessive_bools
+[`struct_field_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#struct_field_names
[`stutter`]: https://rust-lang.github.io/rust-clippy/master/index.html#stutter
[`suboptimal_flops`]: https://rust-lang.github.io/rust-clippy/master/index.html#suboptimal_flops
[`suspicious_arithmetic_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_arithmetic_impl
@@ -5434,6 +5528,7 @@ Released 2018-09-13
[`unknown_clippy_lints`]: https://rust-lang.github.io/rust-clippy/master/index.html#unknown_clippy_lints
[`unnecessary_box_returns`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_box_returns
[`unnecessary_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_cast
+[`unnecessary_fallible_conversions`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_fallible_conversions
[`unnecessary_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_filter_map
[`unnecessary_find_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_find_map
[`unnecessary_fold`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_fold
@@ -5466,6 +5561,7 @@ Released 2018-09-13
[`unstable_as_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#unstable_as_slice
[`unused_async`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_async
[`unused_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_collect
+[`unused_enumerate_index`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_enumerate_index
[`unused_format_specs`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_format_specs
[`unused_io_amount`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_io_amount
[`unused_label`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_label
@@ -5495,6 +5591,7 @@ Released 2018-09-13
[`verbose_bit_mask`]: https://rust-lang.github.io/rust-clippy/master/index.html#verbose_bit_mask
[`verbose_file_reads`]: https://rust-lang.github.io/rust-clippy/master/index.html#verbose_file_reads
[`vtable_address_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#vtable_address_comparisons
+[`waker_clone_wake`]: https://rust-lang.github.io/rust-clippy/master/index.html#waker_clone_wake
[`while_immutable_condition`]: https://rust-lang.github.io/rust-clippy/master/index.html#while_immutable_condition
[`while_let_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#while_let_loop
[`while_let_on_iterator`]: https://rust-lang.github.io/rust-clippy/master/index.html#while_let_on_iterator
@@ -5532,6 +5629,7 @@ Released 2018-09-13
[`single-char-binding-names-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#single-char-binding-names-threshold
[`too-large-for-stack`]: https://doc.rust-lang.org/clippy/lint_configuration.html#too-large-for-stack
[`enum-variant-name-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#enum-variant-name-threshold
+[`struct-field-name-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#struct-field-name-threshold
[`enum-variant-size-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#enum-variant-size-threshold
[`verbose-bit-mask-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#verbose-bit-mask-threshold
[`literal-representation-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#literal-representation-threshold
diff --git a/src/tools/clippy/Cargo.toml b/src/tools/clippy/Cargo.toml
index 66786004f..4b6688a76 100644
--- a/src/tools/clippy/Cargo.toml
+++ b/src/tools/clippy/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "clippy"
-version = "0.1.74"
+version = "0.1.75"
description = "A bunch of helpful lints to avoid common pitfalls in Rust"
repository = "https://github.com/rust-lang/rust-clippy"
readme = "README.md"
@@ -21,13 +21,16 @@ name = "clippy-driver"
path = "src/driver.rs"
[dependencies]
+clippy_config = { path = "clippy_config" }
clippy_lints = { path = "clippy_lints" }
rustc_tools_util = "0.3.0"
tempfile = { version = "3.2", optional = true }
termize = "0.1"
+color-print = "0.3.4"
+anstream = "0.5.0"
[dev-dependencies]
-ui_test = "0.20"
+ui_test = "0.21.2"
tester = "0.9"
regex = "1.5"
toml = "0.7.3"
diff --git a/src/tools/clippy/book/src/development/adding_lints.md b/src/tools/clippy/book/src/development/adding_lints.md
index f6f0c95c7..55c0e105b 100644
--- a/src/tools/clippy/book/src/development/adding_lints.md
+++ b/src/tools/clippy/book/src/development/adding_lints.md
@@ -30,6 +30,7 @@ because that's clearly a non-descriptive name.
- [Documentation](#documentation)
- [Running rustfmt](#running-rustfmt)
- [Debugging](#debugging)
+ - [Conflicting lints](#conflicting-lints)
- [PR Checklist](#pr-checklist)
- [Adding configuration to a lint](#adding-configuration-to-a-lint)
- [Cheat Sheet](#cheat-sheet)
@@ -261,7 +262,7 @@ impl EarlyLintPass for FooFunctions {}
[declare_clippy_lint]: https://github.com/rust-lang/rust-clippy/blob/557f6848bd5b7183f55c1e1522a326e9e1df6030/clippy_lints/src/lib.rs#L60
[example_lint_page]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure
[lint_naming]: https://rust-lang.github.io/rfcs/0344-conventions-galore.html#lints
-[category_level_mapping]: https://github.com/rust-lang/rust-clippy/blob/557f6848bd5b7183f55c1e1522a326e9e1df6030/clippy_lints/src/lib.rs#L110
+[category_level_mapping]: ../index.html
## Lint registration
@@ -612,6 +613,24 @@ output in the `stdout` part.
[`dbg!`]: https://doc.rust-lang.org/std/macro.dbg.html
+## Conflicting lints
+
+There are several lints that deal with the same pattern but suggest different approaches. In other words, some lints
+may suggest modifications that go in the opposite direction to what some other lints already propose for the same
+code, creating conflicting diagnostics.
+
+When you are creating a lint that ends up in this scenario, the following tips should be encouraged to guide
+classification:
+
+* The only case where they should be in the same category is if that category is `restriction`. For example,
+`semicolon_inside_block` and `semicolon_outside_block`.
+* For all the other cases, they should be in different categories with different levels of allowance. For example,
+`implicit_return` (restriction, allow) and `needless_return` (style, warn).
+
+For lints that are in different categories, it is also recommended that at least one of them should be in the
+`restriction` category. The reason for this is that the `restriction` group is the only group where we don't
+recommend to enable the entire set, but cherry pick lints out of.
+
## PR Checklist
Before submitting your PR make sure you followed all the basic requirements:
diff --git a/src/tools/clippy/book/src/lint_configuration.md b/src/tools/clippy/book/src/lint_configuration.md
index b980083f1..841a5b6d0 100644
--- a/src/tools/clippy/book/src/lint_configuration.md
+++ b/src/tools/clippy/book/src/lint_configuration.md
@@ -26,7 +26,7 @@ arithmetic-side-effects-allowed = ["SomeType", "AnotherType"]
A type, say `SomeType`, listed in this configuration has the same behavior of
`["SomeType" , "*"], ["*", "SomeType"]` in `arithmetic_side_effects_allowed_binary`.
-**Default Value:** `{}` (`rustc_data_structures::fx::FxHashSet<String>`)
+**Default Value:** `[]`
---
**Affected lints:**
@@ -49,7 +49,7 @@ Pairs are asymmetric, which means that `["SomeType", "AnotherType"]` is not the
arithmetic-side-effects-allowed-binary = [["SomeType" , "f32"], ["AnotherType", "*"]]
```
-**Default Value:** `[]` (`Vec<[String; 2]>`)
+**Default Value:** `[]`
---
**Affected lints:**
@@ -65,7 +65,7 @@ Suppress checking of the passed type names in unary operations like "negation" (
arithmetic-side-effects-allowed-unary = ["SomeType", "AnotherType"]
```
-**Default Value:** `{}` (`rustc_data_structures::fx::FxHashSet<String>`)
+**Default Value:** `[]`
---
**Affected lints:**
@@ -75,7 +75,7 @@ arithmetic-side-effects-allowed-unary = ["SomeType", "AnotherType"]
## `avoid-breaking-exported-api`
Suppress lints whenever the suggested change would cause breakage for other crates.
-**Default Value:** `true` (`bool`)
+**Default Value:** `true`
---
**Affected lints:**
@@ -98,9 +98,7 @@ Suppress lints whenever the suggested change would cause breakage for other crat
## `msrv`
-The minimum rust version that the project supports
-
-**Default Value:** `None` (`Option<String>`)
+The minimum rust version that the project supports. Defaults to the `rust-version` field in `Cargo.toml`
---
**Affected lints:**
@@ -151,12 +149,13 @@ The minimum rust version that the project supports
* [`type_repetition_in_bounds`](https://rust-lang.github.io/rust-clippy/master/index.html#type_repetition_in_bounds)
* [`tuple_array_conversions`](https://rust-lang.github.io/rust-clippy/master/index.html#tuple_array_conversions)
* [`manual_try_fold`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_try_fold)
+* [`manual_hash_one`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_hash_one)
## `cognitive-complexity-threshold`
The maximum cognitive complexity a function can have
-**Default Value:** `25` (`u64`)
+**Default Value:** `25`
---
**Affected lints:**
@@ -166,7 +165,7 @@ The maximum cognitive complexity a function can have
## `excessive-nesting-threshold`
The maximum amount of nesting a block can reside in
-**Default Value:** `0` (`u64`)
+**Default Value:** `0`
---
**Affected lints:**
@@ -178,7 +177,7 @@ The list of disallowed names to lint about. NB: `bar` is not here since it has l
`".."` can be used as part of the list to indicate that the configured values should be appended to the
default configuration of Clippy. By default, any configuration will replace the default value.
-**Default Value:** `["foo", "baz", "quux"]` (`Vec<String>`)
+**Default Value:** `["foo", "baz", "quux"]`
---
**Affected lints:**
@@ -188,7 +187,7 @@ default configuration of Clippy. By default, any configuration will replace the
## `semicolon-inside-block-ignore-singleline`
Whether to lint only if it's multiline.
-**Default Value:** `false` (`bool`)
+**Default Value:** `false`
---
**Affected lints:**
@@ -198,7 +197,7 @@ Whether to lint only if it's multiline.
## `semicolon-outside-block-ignore-multiline`
Whether to lint only if it's singleline.
-**Default Value:** `false` (`bool`)
+**Default Value:** `false`
---
**Affected lints:**
@@ -212,9 +211,7 @@ default configuration of Clippy. By default, any configuration will replace the
* `doc-valid-idents = ["ClipPy"]` would replace the default list with `["ClipPy"]`.
* `doc-valid-idents = ["ClipPy", ".."]` would append `ClipPy` to the default list.
-Default list:
-
-**Default Value:** `["KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "DirectX", "ECMAScript", "GPLv2", "GPLv3", "GitHub", "GitLab", "IPv4", "IPv6", "ClojureScript", "CoffeeScript", "JavaScript", "PureScript", "TypeScript", "WebAssembly", "NaN", "NaNs", "OAuth", "GraphQL", "OCaml", "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap", "OpenDNS", "WebGL", "TensorFlow", "TrueType", "iOS", "macOS", "FreeBSD", "TeX", "LaTeX", "BibTeX", "BibLaTeX", "MinGW", "CamelCase"]` (`Vec<String>`)
+**Default Value:** `["KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "DirectX", "ECMAScript", "GPLv2", "GPLv3", "GitHub", "GitLab", "IPv4", "IPv6", "ClojureScript", "CoffeeScript", "JavaScript", "PureScript", "TypeScript", "WebAssembly", "NaN", "NaNs", "OAuth", "GraphQL", "OCaml", "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap", "OpenDNS", "WebGL", "TensorFlow", "TrueType", "iOS", "macOS", "FreeBSD", "TeX", "LaTeX", "BibTeX", "BibLaTeX", "MinGW", "CamelCase"]`
---
**Affected lints:**
@@ -224,7 +221,7 @@ Default list:
## `too-many-arguments-threshold`
The maximum number of argument a function or method can have
-**Default Value:** `7` (`u64`)
+**Default Value:** `7`
---
**Affected lints:**
@@ -234,7 +231,7 @@ The maximum number of argument a function or method can have
## `type-complexity-threshold`
The maximum complexity a type can have
-**Default Value:** `250` (`u64`)
+**Default Value:** `250`
---
**Affected lints:**
@@ -244,7 +241,7 @@ The maximum complexity a type can have
## `single-char-binding-names-threshold`
The maximum number of single char bindings a scope may have
-**Default Value:** `4` (`u64`)
+**Default Value:** `4`
---
**Affected lints:**
@@ -254,7 +251,7 @@ The maximum number of single char bindings a scope may have
## `too-large-for-stack`
The maximum size of objects (in bytes) that will be linted. Larger objects are ok on the heap
-**Default Value:** `200` (`u64`)
+**Default Value:** `200`
---
**Affected lints:**
@@ -265,17 +262,27 @@ The maximum size of objects (in bytes) that will be linted. Larger objects are o
## `enum-variant-name-threshold`
The minimum number of enum variants for the lints about variant names to trigger
-**Default Value:** `3` (`u64`)
+**Default Value:** `3`
---
**Affected lints:**
* [`enum_variant_names`](https://rust-lang.github.io/rust-clippy/master/index.html#enum_variant_names)
+## `struct-field-name-threshold`
+The minimum number of struct fields for the lints about field names to trigger
+
+**Default Value:** `3`
+
+---
+**Affected lints:**
+* [`struct_variant_names`](https://rust-lang.github.io/rust-clippy/master/index.html#struct_variant_names)
+
+
## `enum-variant-size-threshold`
The maximum size of an enum's variant to avoid box suggestion
-**Default Value:** `200` (`u64`)
+**Default Value:** `200`
---
**Affected lints:**
@@ -285,7 +292,7 @@ The maximum size of an enum's variant to avoid box suggestion
## `verbose-bit-mask-threshold`
The maximum allowed size of a bit mask before suggesting to use 'trailing_zeros'
-**Default Value:** `1` (`u64`)
+**Default Value:** `1`
---
**Affected lints:**
@@ -295,7 +302,7 @@ The maximum allowed size of a bit mask before suggesting to use 'trailing_zeros'
## `literal-representation-threshold`
The lower bound for linting decimal literals
-**Default Value:** `16384` (`u64`)
+**Default Value:** `16384`
---
**Affected lints:**
@@ -303,9 +310,8 @@ The lower bound for linting decimal literals
## `trivial-copy-size-limit`
-The maximum size (in bytes) to consider a `Copy` type for passing by value instead of by reference.
-
-**Default Value:** `None` (`Option<u64>`)
+The maximum size (in bytes) to consider a `Copy` type for passing by value instead of by
+reference. By default there is no limit
---
**Affected lints:**
@@ -315,7 +321,7 @@ The maximum size (in bytes) to consider a `Copy` type for passing by value inste
## `pass-by-value-size-limit`
The minimum size (in bytes) to consider a type for passing by reference instead of by value.
-**Default Value:** `256` (`u64`)
+**Default Value:** `256`
---
**Affected lints:**
@@ -325,7 +331,7 @@ The minimum size (in bytes) to consider a type for passing by reference instead
## `too-many-lines-threshold`
The maximum number of lines a function or method can have
-**Default Value:** `100` (`u64`)
+**Default Value:** `100`
---
**Affected lints:**
@@ -335,7 +341,7 @@ The maximum number of lines a function or method can have
## `array-size-threshold`
The maximum allowed size for arrays on the stack
-**Default Value:** `512000` (`u64`)
+**Default Value:** `512000`
---
**Affected lints:**
@@ -346,7 +352,7 @@ The maximum allowed size for arrays on the stack
## `stack-size-threshold`
The maximum allowed stack size for functions in bytes
-**Default Value:** `512000` (`u64`)
+**Default Value:** `512000`
---
**Affected lints:**
@@ -356,7 +362,7 @@ The maximum allowed stack size for functions in bytes
## `vec-box-size-threshold`
The size of the boxed type in bytes, where boxing in a `Vec` is allowed
-**Default Value:** `4096` (`u64`)
+**Default Value:** `4096`
---
**Affected lints:**
@@ -366,7 +372,7 @@ The size of the boxed type in bytes, where boxing in a `Vec` is allowed
## `max-trait-bounds`
The maximum number of bounds a trait can have to be linted
-**Default Value:** `3` (`u64`)
+**Default Value:** `3`
---
**Affected lints:**
@@ -376,7 +382,7 @@ The maximum number of bounds a trait can have to be linted
## `max-struct-bools`
The maximum number of bool fields a struct can have
-**Default Value:** `3` (`u64`)
+**Default Value:** `3`
---
**Affected lints:**
@@ -386,7 +392,7 @@ The maximum number of bool fields a struct can have
## `max-fn-params-bools`
The maximum number of bool parameters a function can have
-**Default Value:** `3` (`u64`)
+**Default Value:** `3`
---
**Affected lints:**
@@ -396,7 +402,7 @@ The maximum number of bool parameters a function can have
## `warn-on-all-wildcard-imports`
Whether to allow certain wildcard imports (prelude, super in tests).
-**Default Value:** `false` (`bool`)
+**Default Value:** `false`
---
**Affected lints:**
@@ -406,7 +412,7 @@ Whether to allow certain wildcard imports (prelude, super in tests).
## `disallowed-macros`
The list of disallowed macros, written as fully qualified paths.
-**Default Value:** `[]` (`Vec<crate::utils::conf::DisallowedPath>`)
+**Default Value:** `[]`
---
**Affected lints:**
@@ -416,7 +422,7 @@ The list of disallowed macros, written as fully qualified paths.
## `disallowed-methods`
The list of disallowed methods, written as fully qualified paths.
-**Default Value:** `[]` (`Vec<crate::utils::conf::DisallowedPath>`)
+**Default Value:** `[]`
---
**Affected lints:**
@@ -426,7 +432,7 @@ The list of disallowed methods, written as fully qualified paths.
## `disallowed-types`
The list of disallowed types, written as fully qualified paths.
-**Default Value:** `[]` (`Vec<crate::utils::conf::DisallowedPath>`)
+**Default Value:** `[]`
---
**Affected lints:**
@@ -436,7 +442,7 @@ The list of disallowed types, written as fully qualified paths.
## `unreadable-literal-lint-fractions`
Should the fraction of a decimal be linted to include separators.
-**Default Value:** `true` (`bool`)
+**Default Value:** `true`
---
**Affected lints:**
@@ -446,7 +452,7 @@ Should the fraction of a decimal be linted to include separators.
## `upper-case-acronyms-aggressive`
Enables verbose mode. Triggers if there is more than one uppercase char next to each other
-**Default Value:** `false` (`bool`)
+**Default Value:** `false`
---
**Affected lints:**
@@ -457,7 +463,7 @@ Enables verbose mode. Triggers if there is more than one uppercase char next to
Whether the matches should be considered by the lint, and whether there should
be filtering for common types.
-**Default Value:** `WellKnownTypes` (`crate::manual_let_else::MatchLintBehaviour`)
+**Default Value:** `"WellKnownTypes"`
---
**Affected lints:**
@@ -467,11 +473,11 @@ be filtering for common types.
## `cargo-ignore-publish`
For internal testing only, ignores the current `publish` settings in the Cargo manifest.
-**Default Value:** `false` (`bool`)
+**Default Value:** `false`
---
**Affected lints:**
-* [`_cargo_common_metadata`](https://rust-lang.github.io/rust-clippy/master/index.html#_cargo_common_metadata)
+* [`cargo_common_metadata`](https://rust-lang.github.io/rust-clippy/master/index.html#cargo_common_metadata)
## `standard-macro-braces`
@@ -481,7 +487,7 @@ A `MacroMatcher` can be added like so `{ name = "macro_name", brace = "(" }`. If
could be used with a full path two `MacroMatcher`s have to be added one with the full path
`crate_name::macro_name` and one with just the macro name.
-**Default Value:** `[]` (`Vec<crate::nonstandard_macro_braces::MacroMatcher>`)
+**Default Value:** `[]`
---
**Affected lints:**
@@ -491,7 +497,7 @@ could be used with a full path two `MacroMatcher`s have to be added one with the
## `enforced-import-renames`
The list of imports to always rename, a fully qualified path followed by the rename.
-**Default Value:** `[]` (`Vec<crate::utils::conf::Rename>`)
+**Default Value:** `[]`
---
**Affected lints:**
@@ -501,7 +507,7 @@ The list of imports to always rename, a fully qualified path followed by the ren
## `allowed-scripts`
The list of unicode scripts allowed to be used in the scope.
-**Default Value:** `["Latin"]` (`Vec<String>`)
+**Default Value:** `["Latin"]`
---
**Affected lints:**
@@ -511,7 +517,7 @@ The list of unicode scripts allowed to be used in the scope.
## `enable-raw-pointer-heuristic-for-send`
Whether to apply the raw pointer heuristic to determine if a type is `Send`.
-**Default Value:** `true` (`bool`)
+**Default Value:** `true`
---
**Affected lints:**
@@ -523,7 +529,7 @@ When Clippy suggests using a slice pattern, this is the maximum number of elemen
the slice pattern that is suggested. If more elements are necessary, the lint is suppressed.
For example, `[_, _, _, e, ..]` is a slice pattern with 4 elements.
-**Default Value:** `3` (`u64`)
+**Default Value:** `3`
---
**Affected lints:**
@@ -533,7 +539,7 @@ For example, `[_, _, _, e, ..]` is a slice pattern with 4 elements.
## `await-holding-invalid-types`
-**Default Value:** `[]` (`Vec<crate::utils::conf::DisallowedPath>`)
+**Default Value:** `[]`
---
**Affected lints:**
@@ -543,7 +549,7 @@ For example, `[_, _, _, e, ..]` is a slice pattern with 4 elements.
## `max-include-file-size`
The maximum size of a file included via `include_bytes!()` or `include_str!()`, in bytes
-**Default Value:** `1000000` (`u64`)
+**Default Value:** `1000000`
---
**Affected lints:**
@@ -553,7 +559,7 @@ The maximum size of a file included via `include_bytes!()` or `include_str!()`,
## `allow-expect-in-tests`
Whether `expect` should be allowed in test functions or `#[cfg(test)]`
-**Default Value:** `false` (`bool`)
+**Default Value:** `false`
---
**Affected lints:**
@@ -563,7 +569,7 @@ Whether `expect` should be allowed in test functions or `#[cfg(test)]`
## `allow-unwrap-in-tests`
Whether `unwrap` should be allowed in test functions or `#[cfg(test)]`
-**Default Value:** `false` (`bool`)
+**Default Value:** `false`
---
**Affected lints:**
@@ -573,7 +579,7 @@ Whether `unwrap` should be allowed in test functions or `#[cfg(test)]`
## `allow-dbg-in-tests`
Whether `dbg!` should be allowed in test functions or `#[cfg(test)]`
-**Default Value:** `false` (`bool`)
+**Default Value:** `false`
---
**Affected lints:**
@@ -583,7 +589,7 @@ Whether `dbg!` should be allowed in test functions or `#[cfg(test)]`
## `allow-print-in-tests`
Whether print macros (ex. `println!`) should be allowed in test functions or `#[cfg(test)]`
-**Default Value:** `false` (`bool`)
+**Default Value:** `false`
---
**Affected lints:**
@@ -594,7 +600,7 @@ Whether print macros (ex. `println!`) should be allowed in test functions or `#[
## `large-error-threshold`
The maximum size of the `Err`-variant in a `Result` returned from a function
-**Default Value:** `128` (`u64`)
+**Default Value:** `128`
---
**Affected lints:**
@@ -605,7 +611,7 @@ The maximum size of the `Err`-variant in a `Result` returned from a function
A list of paths to types that should be treated like `Arc`, i.e. ignored but
for the generic parameters for determining interior mutability
-**Default Value:** `["bytes::Bytes"]` (`Vec<String>`)
+**Default Value:** `["bytes::Bytes"]`
---
**Affected lints:**
@@ -616,7 +622,7 @@ for the generic parameters for determining interior mutability
## `allow-mixed-uninlined-format-args`
Whether to allow mixed uninlined format args, e.g. `format!("{} {}", a, foo.bar)`
-**Default Value:** `true` (`bool`)
+**Default Value:** `true`
---
**Affected lints:**
@@ -630,7 +636,7 @@ suggested counterparts are unavailable in constant code. This
configuration will cause restriction lints to trigger even
if no suggestion can be made.
-**Default Value:** `false` (`bool`)
+**Default Value:** `false`
---
**Affected lints:**
@@ -641,7 +647,7 @@ if no suggestion can be made.
Whether to **only** check for missing documentation in items visible within the current
crate. For example, `pub(crate)` items.
-**Default Value:** `false` (`bool`)
+**Default Value:** `false`
---
**Affected lints:**
@@ -651,7 +657,7 @@ crate. For example, `pub(crate)` items.
## `future-size-threshold`
The maximum byte size a `Future` can have, before it triggers the `clippy::large_futures` lint
-**Default Value:** `16384` (`u64`)
+**Default Value:** `16384`
---
**Affected lints:**
@@ -661,7 +667,7 @@ The maximum byte size a `Future` can have, before it triggers the `clippy::large
## `unnecessary-box-size`
The byte size a `T` in `Box<T>` can have, below which it triggers the `clippy::unnecessary_box` lint
-**Default Value:** `128` (`u64`)
+**Default Value:** `128`
---
**Affected lints:**
@@ -671,7 +677,7 @@ The byte size a `T` in `Box<T>` can have, below which it triggers the `clippy::u
## `allow-private-module-inception`
Whether to allow module inception if it's not public.
-**Default Value:** `false` (`bool`)
+**Default Value:** `false`
---
**Affected lints:**
@@ -683,7 +689,7 @@ Allowed names below the minimum allowed characters. The value `".."` can be used
the list to indicate, that the configured values should be appended to the default
configuration of Clippy. By default, any configuration will replace the default value.
-**Default Value:** `{"j", "z", "i", "y", "n", "x", "w"}` (`rustc_data_structures::fx::FxHashSet<String>`)
+**Default Value:** `["j", "z", "i", "y", "n", "x", "w"]`
---
**Affected lints:**
@@ -693,7 +699,7 @@ configuration of Clippy. By default, any configuration will replace the default
## `min-ident-chars-threshold`
Minimum chars an ident can have, anything below or equal to this will be linted.
-**Default Value:** `1` (`u64`)
+**Default Value:** `1`
---
**Affected lints:**
@@ -703,7 +709,7 @@ Minimum chars an ident can have, anything below or equal to this will be linted.
## `accept-comment-above-statement`
Whether to accept a safety comment to be placed above the statement containing the `unsafe` block
-**Default Value:** `true` (`bool`)
+**Default Value:** `true`
---
**Affected lints:**
@@ -713,7 +719,7 @@ Whether to accept a safety comment to be placed above the statement containing t
## `accept-comment-above-attributes`
Whether to accept a safety comment to be placed above the attributes for the `unsafe` block
-**Default Value:** `true` (`bool`)
+**Default Value:** `true`
---
**Affected lints:**
@@ -723,7 +729,7 @@ Whether to accept a safety comment to be placed above the attributes for the `un
## `allow-one-hash-in-raw-strings`
Whether to allow `r#""#` when `r""` can be used
-**Default Value:** `false` (`bool`)
+**Default Value:** `false`
---
**Affected lints:**
@@ -734,7 +740,7 @@ Whether to allow `r#""#` when `r""` can be used
The maximum number of segments a path can have before being linted, anything above this will
be linted.
-**Default Value:** `2` (`u64`)
+**Default Value:** `2`
---
**Affected lints:**
@@ -744,7 +750,7 @@ be linted.
## `absolute-paths-allowed-crates`
Which crates to allow absolute paths from
-**Default Value:** `{}` (`rustc_data_structures::fx::FxHashSet<String>`)
+**Default Value:** `[]`
---
**Affected lints:**
@@ -754,7 +760,7 @@ Which crates to allow absolute paths from
## `allowed-dotfiles`
Additional dotfiles (files or directories starting with a dot) to allow
-**Default Value:** `{}` (`rustc_data_structures::fx::FxHashSet<String>`)
+**Default Value:** `[]`
---
**Affected lints:**
@@ -763,7 +769,7 @@ Additional dotfiles (files or directories starting with a dot) to allow
## `enforce-iter-loop-reborrow`
#### Example
-```
+```no_run
let mut vec = vec![1, 2, 3];
let rmvec = &mut vec;
for _ in rmvec.iter() {}
@@ -771,14 +777,14 @@ for _ in rmvec.iter_mut() {}
```
Use instead:
-```
+```no_run
let mut vec = vec![1, 2, 3];
let rmvec = &mut vec;
for _ in &*rmvec {}
for _ in &mut *rmvec {}
```
-**Default Value:** `false` (`bool`)
+**Default Value:** `false`
---
**Affected lints:**
diff --git a/src/tools/clippy/clippy_config/Cargo.toml b/src/tools/clippy/clippy_config/Cargo.toml
new file mode 100644
index 000000000..2d41087b5
--- /dev/null
+++ b/src/tools/clippy/clippy_config/Cargo.toml
@@ -0,0 +1,21 @@
+[package]
+name = "clippy_config"
+version = "0.1.75"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+rustc-semver = "1.1"
+serde = { version = "1.0", features = ["derive"] }
+toml = "0.7.3"
+
+[dev-dependencies]
+walkdir = "2.3"
+
+[features]
+deny-warnings = []
+
+[package.metadata.rust-analyzer]
+# This crate uses #[feature(rustc_private)]
+rustc_private = true
diff --git a/src/tools/clippy/clippy_lints/src/utils/conf.rs b/src/tools/clippy/clippy_config/src/conf.rs
index 75c3c7a95..472597769 100644
--- a/src/tools/clippy/clippy_lints/src/utils/conf.rs
+++ b/src/tools/clippy/clippy_config/src/conf.rs
@@ -1,15 +1,16 @@
-//! Read configurations files.
-
-#![allow(clippy::module_name_repetitions)]
-
+use crate::msrvs::Msrv;
+use crate::types::{DisallowedPath, MacroMatcher, MatchLintBehaviour, Rename};
+use crate::ClippyConfiguration;
+use rustc_data_structures::fx::FxHashSet;
use rustc_session::Session;
use rustc_span::{BytePos, Pos, SourceFile, Span, SyntaxContext};
-use serde::de::{Deserializer, IgnoredAny, IntoDeserializer, MapAccess, Visitor};
-use serde::Deserialize;
+use serde::de::{IgnoredAny, IntoDeserializer, MapAccess, Visitor};
+use serde::{Deserialize, Deserializer, Serialize};
use std::fmt::{Debug, Display, Formatter};
use std::ops::Range;
-use std::path::{Path, PathBuf};
+use std::path::PathBuf;
use std::str::FromStr;
+use std::sync::OnceLock;
use std::{cmp, env, fmt, fs, io};
#[rustfmt::skip]
@@ -37,111 +38,74 @@ const DEFAULT_DOC_VALID_IDENTS: &[&str] = &[
const DEFAULT_DISALLOWED_NAMES: &[&str] = &["foo", "baz", "quux"];
const DEFAULT_ALLOWED_IDENTS_BELOW_MIN_CHARS: &[&str] = &["i", "j", "x", "y", "z", "w", "n"];
-/// Holds information used by `MISSING_ENFORCED_IMPORT_RENAMES` lint.
-#[derive(Clone, Debug, Deserialize)]
-pub struct Rename {
- pub path: String,
- pub rename: String,
-}
-
-#[derive(Clone, Debug, Deserialize)]
-#[serde(untagged)]
-pub enum DisallowedPath {
- Simple(String),
- WithReason { path: String, reason: Option<String> },
-}
-
-impl DisallowedPath {
- pub fn path(&self) -> &str {
- let (Self::Simple(path) | Self::WithReason { path, .. }) = self;
-
- path
- }
-
- pub fn reason(&self) -> Option<String> {
- match self {
- Self::WithReason {
- reason: Some(reason), ..
- } => Some(format!("{reason} (from clippy.toml)")),
- _ => None,
- }
- }
-}
-
/// Conf with parse errors
#[derive(Default)]
-pub struct TryConf {
- pub conf: Conf,
- pub errors: Vec<ConfError>,
- pub warnings: Vec<ConfError>,
+struct TryConf {
+ conf: Conf,
+ errors: Vec<ConfError>,
+ warnings: Vec<ConfError>,
}
impl TryConf {
fn from_toml_error(file: &SourceFile, error: &toml::de::Error) -> Self {
- ConfError::from_toml(file, error).into()
- }
-}
-
-impl From<ConfError> for TryConf {
- fn from(value: ConfError) -> Self {
Self {
conf: Conf::default(),
- errors: vec![value],
+ errors: vec![ConfError::from_toml(file, error)],
warnings: vec![],
}
}
}
-impl From<io::Error> for TryConf {
- fn from(value: io::Error) -> Self {
- ConfError::from(value).into()
- }
-}
-
#[derive(Debug)]
-pub struct ConfError {
- pub message: String,
- pub span: Option<Span>,
+struct ConfError {
+ message: String,
+ span: Span,
}
impl ConfError {
fn from_toml(file: &SourceFile, error: &toml::de::Error) -> Self {
- if let Some(span) = error.span() {
- Self::spanned(file, error.message(), span)
- } else {
- Self {
- message: error.message().to_string(),
- span: None,
- }
- }
+ let span = error.span().unwrap_or(0..file.source_len.0 as usize);
+ Self::spanned(file, error.message(), span)
}
fn spanned(file: &SourceFile, message: impl Into<String>, span: Range<usize>) -> Self {
Self {
message: message.into(),
- span: Some(Span::new(
+ span: Span::new(
file.start_pos + BytePos::from_usize(span.start),
file.start_pos + BytePos::from_usize(span.end),
SyntaxContext::root(),
None,
- )),
+ ),
}
}
}
-impl From<io::Error> for ConfError {
- fn from(value: io::Error) -> Self {
- Self {
- message: value.to_string(),
- span: None,
- }
- }
+macro_rules! wrap_option {
+ () => {
+ None
+ };
+ ($x:literal) => {
+ Some($x)
+ };
+}
+
+macro_rules! default_text {
+ ($value:expr) => {{
+ let mut text = String::new();
+ $value.serialize(toml::ser::ValueSerializer::new(&mut text)).unwrap();
+ text
+ }};
+ ($value:expr, $override:expr) => {
+ $override.to_string()
+ };
}
macro_rules! define_Conf {
($(
$(#[doc = $doc:literal])+
$(#[conf_deprecated($dep:literal, $new_conf:ident)])?
+ $(#[default_text = $default_text:expr])?
($name:ident: $ty:ty = $default:expr),
)*) => {
/// Clippy lint configuration
@@ -150,6 +114,7 @@ macro_rules! define_Conf {
}
mod defaults {
+ use super::*;
$(pub fn $name() -> $ty { $default })*
}
@@ -216,31 +181,21 @@ macro_rules! define_Conf {
}
}
- pub mod metadata {
- use crate::utils::ClippyConfiguration;
-
- macro_rules! wrap_option {
- () => (None);
- ($x:literal) => (Some($x));
- }
-
- pub fn get_configuration_metadata() -> Vec<ClippyConfiguration> {
- vec![
- $(
- {
- let deprecation_reason = wrap_option!($($dep)?);
-
- ClippyConfiguration::new(
- stringify!($name),
- stringify!($ty),
- format!("{:?}", super::defaults::$name()),
- concat!($($doc, '\n',)*),
- deprecation_reason,
- )
- },
- )+
- ]
- }
+ pub fn get_configuration_metadata() -> Vec<ClippyConfiguration> {
+ vec![
+ $(
+ {
+ let deprecation_reason = wrap_option!($($dep)?);
+
+ ClippyConfiguration::new(
+ stringify!($name),
+ default_text!(defaults::$name() $(, $default_text)?),
+ concat!($($doc, '\n',)*),
+ deprecation_reason,
+ )
+ },
+ )+
+ ]
}
};
}
@@ -262,7 +217,7 @@ define_Conf! {
///
/// A type, say `SomeType`, listed in this configuration has the same behavior of
/// `["SomeType" , "*"], ["*", "SomeType"]` in `arithmetic_side_effects_allowed_binary`.
- (arithmetic_side_effects_allowed: rustc_data_structures::fx::FxHashSet<String> = <_>::default()),
+ (arithmetic_side_effects_allowed: FxHashSet<String> = <_>::default()),
/// Lint: ARITHMETIC_SIDE_EFFECTS.
///
/// Suppress checking of the passed type pair names in binary operations like addition or
@@ -289,15 +244,16 @@ define_Conf! {
/// ```toml
/// arithmetic-side-effects-allowed-unary = ["SomeType", "AnotherType"]
/// ```
- (arithmetic_side_effects_allowed_unary: rustc_data_structures::fx::FxHashSet<String> = <_>::default()),
+ (arithmetic_side_effects_allowed_unary: FxHashSet<String> = <_>::default()),
/// Lint: ENUM_VARIANT_NAMES, LARGE_TYPES_PASSED_BY_VALUE, TRIVIALLY_COPY_PASS_BY_REF, UNNECESSARY_WRAPS, UNUSED_SELF, UPPER_CASE_ACRONYMS, WRONG_SELF_CONVENTION, BOX_COLLECTION, REDUNDANT_ALLOCATION, RC_BUFFER, VEC_BOX, OPTION_OPTION, LINKEDLIST, RC_MUTEX, UNNECESSARY_BOX_RETURNS, SINGLE_CALL_FN.
///
/// Suppress lints whenever the suggested change would cause breakage for other crates.
(avoid_breaking_exported_api: bool = true),
- /// Lint: MANUAL_SPLIT_ONCE, MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, OPTION_MAP_UNWRAP_OR, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR, IF_THEN_SOME_ELSE_NONE, APPROX_CONSTANT, DEPRECATED_CFG_ATTR, INDEX_REFUTABLE_SLICE, MAP_CLONE, BORROW_AS_PTR, MANUAL_BITS, ERR_EXPECT, CAST_ABS_TO_UNSIGNED, UNINLINED_FORMAT_ARGS, MANUAL_CLAMP, MANUAL_LET_ELSE, UNCHECKED_DURATION_SUBTRACTION, COLLAPSIBLE_STR_REPLACE, SEEK_FROM_CURRENT, SEEK_REWIND, UNNECESSARY_LAZY_EVALUATIONS, TRANSMUTE_PTR_TO_REF, ALMOST_COMPLETE_RANGE, NEEDLESS_BORROW, DERIVABLE_IMPLS, MANUAL_IS_ASCII_CHECK, MANUAL_REM_EUCLID, MANUAL_RETAIN, TYPE_REPETITION_IN_BOUNDS, TUPLE_ARRAY_CONVERSIONS, MANUAL_TRY_FOLD.
+ /// Lint: MANUAL_SPLIT_ONCE, MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, OPTION_MAP_UNWRAP_OR, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR, IF_THEN_SOME_ELSE_NONE, APPROX_CONSTANT, DEPRECATED_CFG_ATTR, INDEX_REFUTABLE_SLICE, MAP_CLONE, BORROW_AS_PTR, MANUAL_BITS, ERR_EXPECT, CAST_ABS_TO_UNSIGNED, UNINLINED_FORMAT_ARGS, MANUAL_CLAMP, MANUAL_LET_ELSE, UNCHECKED_DURATION_SUBTRACTION, COLLAPSIBLE_STR_REPLACE, SEEK_FROM_CURRENT, SEEK_REWIND, UNNECESSARY_LAZY_EVALUATIONS, TRANSMUTE_PTR_TO_REF, ALMOST_COMPLETE_RANGE, NEEDLESS_BORROW, DERIVABLE_IMPLS, MANUAL_IS_ASCII_CHECK, MANUAL_REM_EUCLID, MANUAL_RETAIN, TYPE_REPETITION_IN_BOUNDS, TUPLE_ARRAY_CONVERSIONS, MANUAL_TRY_FOLD, MANUAL_HASH_ONE.
///
- /// The minimum rust version that the project supports
- (msrv: Option<String> = None),
+ /// The minimum rust version that the project supports. Defaults to the `rust-version` field in `Cargo.toml`
+ #[default_text = ""]
+ (msrv: Msrv = Msrv::empty()),
/// DEPRECATED LINT: BLACKLISTED_NAME.
///
/// Use the Disallowed Names lint instead
@@ -321,7 +277,7 @@ define_Conf! {
/// The list of disallowed names to lint about. NB: `bar` is not here since it has legitimate uses. The value
/// `".."` can be used as part of the list to indicate that the configured values should be appended to the
/// default configuration of Clippy. By default, any configuration will replace the default value.
- (disallowed_names: Vec<String> = super::DEFAULT_DISALLOWED_NAMES.iter().map(ToString::to_string).collect()),
+ (disallowed_names: Vec<String> = DEFAULT_DISALLOWED_NAMES.iter().map(ToString::to_string).collect()),
/// Lint: SEMICOLON_INSIDE_BLOCK.
///
/// Whether to lint only if it's multiline.
@@ -337,9 +293,7 @@ define_Conf! {
/// default configuration of Clippy. By default, any configuration will replace the default value. For example:
/// * `doc-valid-idents = ["ClipPy"]` would replace the default list with `["ClipPy"]`.
/// * `doc-valid-idents = ["ClipPy", ".."]` would append `ClipPy` to the default list.
- ///
- /// Default list:
- (doc_valid_idents: Vec<String> = super::DEFAULT_DOC_VALID_IDENTS.iter().map(ToString::to_string).collect()),
+ (doc_valid_idents: Vec<String> = DEFAULT_DOC_VALID_IDENTS.iter().map(ToString::to_string).collect()),
/// Lint: TOO_MANY_ARGUMENTS.
///
/// The maximum number of argument a function or method can have
@@ -360,6 +314,10 @@ define_Conf! {
///
/// The minimum number of enum variants for the lints about variant names to trigger
(enum_variant_name_threshold: u64 = 3),
+ /// Lint: STRUCT_VARIANT_NAMES.
+ ///
+ /// The minimum number of struct fields for the lints about field names to trigger
+ (struct_field_name_threshold: u64 = 3),
/// Lint: LARGE_ENUM_VARIANT.
///
/// The maximum size of an enum's variant to avoid box suggestion
@@ -374,7 +332,9 @@ define_Conf! {
(literal_representation_threshold: u64 = 16384),
/// Lint: TRIVIALLY_COPY_PASS_BY_REF.
///
- /// The maximum size (in bytes) to consider a `Copy` type for passing by value instead of by reference.
+ /// The maximum size (in bytes) to consider a `Copy` type for passing by value instead of by
+ /// reference. By default there is no limit
+ #[default_text = ""]
(trivial_copy_size_limit: Option<u64> = None),
/// Lint: LARGE_TYPES_PASSED_BY_VALUE.
///
@@ -415,15 +375,15 @@ define_Conf! {
/// Lint: DISALLOWED_MACROS.
///
/// The list of disallowed macros, written as fully qualified paths.
- (disallowed_macros: Vec<crate::utils::conf::DisallowedPath> = Vec::new()),
+ (disallowed_macros: Vec<DisallowedPath> = Vec::new()),
/// Lint: DISALLOWED_METHODS.
///
/// The list of disallowed methods, written as fully qualified paths.
- (disallowed_methods: Vec<crate::utils::conf::DisallowedPath> = Vec::new()),
+ (disallowed_methods: Vec<DisallowedPath> = Vec::new()),
/// Lint: DISALLOWED_TYPES.
///
/// The list of disallowed types, written as fully qualified paths.
- (disallowed_types: Vec<crate::utils::conf::DisallowedPath> = Vec::new()),
+ (disallowed_types: Vec<DisallowedPath> = Vec::new()),
/// Lint: UNREADABLE_LITERAL.
///
/// Should the fraction of a decimal be linted to include separators.
@@ -436,9 +396,8 @@ define_Conf! {
///
/// Whether the matches should be considered by the lint, and whether there should
/// be filtering for common types.
- (matches_for_let_else: crate::manual_let_else::MatchLintBehaviour =
- crate::manual_let_else::MatchLintBehaviour::WellKnownTypes),
- /// Lint: _CARGO_COMMON_METADATA.
+ (matches_for_let_else: MatchLintBehaviour = MatchLintBehaviour::WellKnownTypes),
+ /// Lint: CARGO_COMMON_METADATA.
///
/// For internal testing only, ignores the current `publish` settings in the Cargo manifest.
(cargo_ignore_publish: bool = false),
@@ -449,11 +408,11 @@ define_Conf! {
/// A `MacroMatcher` can be added like so `{ name = "macro_name", brace = "(" }`. If the macro
/// could be used with a full path two `MacroMatcher`s have to be added one with the full path
/// `crate_name::macro_name` and one with just the macro name.
- (standard_macro_braces: Vec<crate::nonstandard_macro_braces::MacroMatcher> = Vec::new()),
+ (standard_macro_braces: Vec<MacroMatcher> = Vec::new()),
/// Lint: MISSING_ENFORCED_IMPORT_RENAMES.
///
/// The list of imports to always rename, a fully qualified path followed by the rename.
- (enforced_import_renames: Vec<crate::utils::conf::Rename> = Vec::new()),
+ (enforced_import_renames: Vec<Rename> = Vec::new()),
/// Lint: DISALLOWED_SCRIPT_IDENTS.
///
/// The list of unicode scripts allowed to be used in the scope.
@@ -469,7 +428,7 @@ define_Conf! {
/// For example, `[_, _, _, e, ..]` is a slice pattern with 4 elements.
(max_suggested_slice_pattern_length: u64 = 3),
/// Lint: AWAIT_HOLDING_INVALID_TYPE.
- (await_holding_invalid_types: Vec<crate::utils::conf::DisallowedPath> = Vec::new()),
+ (await_holding_invalid_types: Vec<DisallowedPath> = Vec::new()),
/// Lint: LARGE_INCLUDE_FILE.
///
/// The maximum size of a file included via `include_bytes!()` or `include_str!()`, in bytes
@@ -533,8 +492,8 @@ define_Conf! {
/// Allowed names below the minimum allowed characters. The value `".."` can be used as part of
/// the list to indicate, that the configured values should be appended to the default
/// configuration of Clippy. By default, any configuration will replace the default value.
- (allowed_idents_below_min_chars: rustc_data_structures::fx::FxHashSet<String> =
- super::DEFAULT_ALLOWED_IDENTS_BELOW_MIN_CHARS.iter().map(ToString::to_string).collect()),
+ (allowed_idents_below_min_chars: FxHashSet<String> =
+ DEFAULT_ALLOWED_IDENTS_BELOW_MIN_CHARS.iter().map(ToString::to_string).collect()),
/// Lint: MIN_IDENT_CHARS.
///
/// Minimum chars an ident can have, anything below or equal to this will be linted.
@@ -559,19 +518,17 @@ define_Conf! {
/// Lint: ABSOLUTE_PATHS.
///
/// Which crates to allow absolute paths from
- (absolute_paths_allowed_crates: rustc_data_structures::fx::FxHashSet<String> =
- rustc_data_structures::fx::FxHashSet::default()),
+ (absolute_paths_allowed_crates: FxHashSet<String> = FxHashSet::default()),
/// Lint: PATH_ENDS_WITH_EXT.
///
/// Additional dotfiles (files or directories starting with a dot) to allow
- (allowed_dotfiles: rustc_data_structures::fx::FxHashSet<String> =
- rustc_data_structures::fx::FxHashSet::default()),
+ (allowed_dotfiles: FxHashSet<String> = FxHashSet::default()),
/// Lint: EXPLICIT_ITER_LOOP
///
/// Whether to recommend using implicit into iter for reborrowed values.
///
/// #### Example
- /// ```
+ /// ```no_run
/// let mut vec = vec![1, 2, 3];
/// let rmvec = &mut vec;
/// for _ in rmvec.iter() {}
@@ -579,7 +536,7 @@ define_Conf! {
/// ```
///
/// Use instead:
- /// ```
+ /// ```no_run
/// let mut vec = vec![1, 2, 3];
/// let rmvec = &mut vec;
/// for _ in &*rmvec {}
@@ -641,15 +598,8 @@ pub fn lookup_conf_file() -> io::Result<(Option<PathBuf>, Vec<String>)> {
}
}
-/// Read the `toml` configuration file.
-///
-/// In case of error, the function tries to continue as much as possible.
-pub fn read(sess: &Session, path: &Path) -> TryConf {
- let file = match sess.source_map().load_file(path) {
- Err(e) => return e.into(),
- Ok(file) => file,
- };
- match toml::de::Deserializer::new(file.src.as_ref().unwrap()).deserialize_map(ConfVisitor(&file)) {
+fn deserialize(file: &SourceFile) -> TryConf {
+ match toml::de::Deserializer::new(file.src.as_ref().unwrap()).deserialize_map(ConfVisitor(file)) {
Ok(mut conf) => {
extend_vec_if_indicator_present(&mut conf.conf.doc_valid_idents, DEFAULT_DOC_VALID_IDENTS);
extend_vec_if_indicator_present(&mut conf.conf.disallowed_names, DEFAULT_DISALLOWED_NAMES);
@@ -662,7 +612,7 @@ pub fn read(sess: &Session, path: &Path) -> TryConf {
conf
},
- Err(e) => TryConf::from_toml_error(&file, &e),
+ Err(e) => TryConf::from_toml_error(file, &e),
}
}
@@ -672,6 +622,60 @@ fn extend_vec_if_indicator_present(vec: &mut Vec<String>, default: &[&str]) {
}
}
+impl Conf {
+ pub fn read(sess: &Session, path: &io::Result<(Option<PathBuf>, Vec<String>)>) -> &'static Conf {
+ static CONF: OnceLock<Conf> = OnceLock::new();
+ CONF.get_or_init(|| Conf::read_inner(sess, path))
+ }
+
+ fn read_inner(sess: &Session, path: &io::Result<(Option<PathBuf>, Vec<String>)>) -> Conf {
+ match path {
+ Ok((_, warnings)) => {
+ for warning in warnings {
+ sess.warn(warning.clone());
+ }
+ },
+ Err(error) => {
+ sess.err(format!("error finding Clippy's configuration file: {error}"));
+ },
+ }
+
+ let TryConf {
+ mut conf,
+ errors,
+ warnings,
+ } = match path {
+ Ok((Some(path), _)) => match sess.source_map().load_file(path) {
+ Ok(file) => deserialize(&file),
+ Err(error) => {
+ sess.err(format!("failed to read `{}`: {error}", path.display()));
+ TryConf::default()
+ },
+ },
+ _ => TryConf::default(),
+ };
+
+ conf.msrv.read_cargo(sess);
+
+ // all conf errors are non-fatal, we just use the default conf in case of error
+ for error in errors {
+ sess.span_err(
+ error.span,
+ format!("error reading Clippy's configuration file: {}", error.message),
+ );
+ }
+
+ for warning in warnings {
+ sess.span_warn(
+ warning.span,
+ format!("error reading Clippy's configuration file: {}", warning.message),
+ );
+ }
+
+ conf
+ }
+}
+
const SEPARATOR_WIDTH: usize = 4;
#[derive(Debug)]
@@ -744,3 +748,44 @@ fn calculate_dimensions(fields: &[&str]) -> (usize, Vec<usize>) {
(rows, column_widths)
}
+
+#[cfg(test)]
+mod tests {
+ use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+ use serde::de::IgnoredAny;
+ use std::fs;
+ use walkdir::WalkDir;
+
+ #[test]
+ fn configs_are_tested() {
+ let mut names: FxHashSet<String> = crate::get_configuration_metadata()
+ .into_iter()
+ .map(|meta| meta.name.replace('_', "-"))
+ .collect();
+
+ let toml_files = WalkDir::new("../tests")
+ .into_iter()
+ .map(Result::unwrap)
+ .filter(|entry| entry.file_name() == "clippy.toml");
+
+ for entry in toml_files {
+ let file = fs::read_to_string(entry.path()).unwrap();
+ #[allow(clippy::zero_sized_map_values)]
+ if let Ok(map) = toml::from_str::<FxHashMap<String, IgnoredAny>>(&file) {
+ for name in map.keys() {
+ names.remove(name.as_str());
+ }
+ }
+ }
+
+ assert!(
+ names.remove("allow-one-hash-in-raw-strings"),
+ "remove this when #11481 is fixed"
+ );
+
+ assert!(
+ names.is_empty(),
+ "Configuration variable lacks test: {names:?}\nAdd a test to `tests/ui-toml`"
+ );
+ }
+}
diff --git a/src/tools/clippy/clippy_config/src/lib.rs b/src/tools/clippy/clippy_config/src/lib.rs
new file mode 100644
index 000000000..f5dcb16d6
--- /dev/null
+++ b/src/tools/clippy/clippy_config/src/lib.rs
@@ -0,0 +1,23 @@
+#![feature(rustc_private, let_chains)]
+#![cfg_attr(feature = "deny-warnings", deny(warnings))]
+#![warn(rust_2018_idioms, unused_lifetimes)]
+#![allow(
+ clippy::must_use_candidate,
+ clippy::missing_panics_doc,
+ rustc::untranslatable_diagnostic_trivial
+)]
+
+extern crate rustc_ast;
+extern crate rustc_data_structures;
+#[allow(unused_extern_crates)]
+extern crate rustc_driver;
+extern crate rustc_session;
+extern crate rustc_span;
+
+mod conf;
+mod metadata;
+pub mod msrvs;
+pub mod types;
+
+pub use conf::{get_configuration_metadata, lookup_conf_file, Conf};
+pub use metadata::ClippyConfiguration;
diff --git a/src/tools/clippy/clippy_config/src/metadata.rs b/src/tools/clippy/clippy_config/src/metadata.rs
new file mode 100644
index 000000000..2451fbc91
--- /dev/null
+++ b/src/tools/clippy/clippy_config/src/metadata.rs
@@ -0,0 +1,116 @@
+use std::fmt::{self, Write};
+
+#[derive(Debug, Clone, Default)]
+pub struct ClippyConfiguration {
+ pub name: String,
+ pub default: String,
+ pub lints: Vec<String>,
+ pub doc: String,
+ pub deprecation_reason: Option<&'static str>,
+}
+
+impl fmt::Display for ClippyConfiguration {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "- `{}`: {}", self.name, self.doc)?;
+ if !self.default.is_empty() {
+ write!(f, " (default: `{}`)", self.default)?;
+ }
+ Ok(())
+ }
+}
+
+impl ClippyConfiguration {
+ pub fn new(
+ name: &'static str,
+ default: String,
+ doc_comment: &'static str,
+ deprecation_reason: Option<&'static str>,
+ ) -> Self {
+ let (lints, doc) = parse_config_field_doc(doc_comment)
+ .unwrap_or_else(|| (vec![], "[ERROR] MALFORMED DOC COMMENT".to_string()));
+
+ Self {
+ name: to_kebab(name),
+ lints,
+ doc,
+ default,
+ deprecation_reason,
+ }
+ }
+
+ pub fn to_markdown_paragraph(&self) -> String {
+ let mut out = format!(
+ "## `{}`\n{}\n\n",
+ self.name,
+ self.doc
+ .lines()
+ .map(|line| line.strip_prefix(" ").unwrap_or(line))
+ .collect::<Vec<_>>()
+ .join("\n"),
+ );
+
+ if !self.default.is_empty() {
+ write!(out, "**Default Value:** `{}`\n\n", self.default).unwrap();
+ }
+
+ write!(
+ out,
+ "---\n**Affected lints:**\n{}\n\n",
+ self.lints
+ .iter()
+ .map(|name| name.to_string().split_whitespace().next().unwrap().to_string())
+ .map(|name| format!("* [`{name}`](https://rust-lang.github.io/rust-clippy/master/index.html#{name})"))
+ .collect::<Vec<_>>()
+ .join("\n"),
+ )
+ .unwrap();
+
+ out
+ }
+
+ pub fn to_markdown_link(&self) -> String {
+ const BOOK_CONFIGS_PATH: &str = "https://doc.rust-lang.org/clippy/lint_configuration.html";
+ format!("[`{}`]: {BOOK_CONFIGS_PATH}#{}", self.name, self.name)
+ }
+}
+
+/// This parses the field documentation of the config struct.
+///
+/// ```rust, ignore
+/// parse_config_field_doc(cx, "Lint: LINT_NAME_1, LINT_NAME_2. Papa penguin, papa penguin")
+/// ```
+///
+/// Would yield:
+/// ```rust, ignore
+/// Some(["lint_name_1", "lint_name_2"], "Papa penguin, papa penguin")
+/// ```
+fn parse_config_field_doc(doc_comment: &str) -> Option<(Vec<String>, String)> {
+ const DOC_START: &str = " Lint: ";
+ if doc_comment.starts_with(DOC_START)
+ && let Some(split_pos) = doc_comment.find('.')
+ {
+ let mut doc_comment = doc_comment.to_string();
+ let mut documentation = doc_comment.split_off(split_pos);
+
+ // Extract lints
+ doc_comment.make_ascii_lowercase();
+ let lints: Vec<String> = doc_comment
+ .split_off(DOC_START.len())
+ .split(", ")
+ .map(str::to_string)
+ .collect();
+
+ // Format documentation correctly
+ // split off leading `.` from lint name list and indent for correct formatting
+ documentation = documentation.trim_start_matches('.').trim().replace("\n ", "\n ");
+
+ Some((lints, documentation))
+ } else {
+ None
+ }
+}
+
+/// Transforms a given `snake_case_string` to a tasty `kebab-case-string`
+fn to_kebab(config_name: &str) -> String {
+ config_name.replace('_', "-")
+}
diff --git a/src/tools/clippy/clippy_utils/src/msrvs.rs b/src/tools/clippy/clippy_config/src/msrvs.rs
index 0faff05eb..011d54629 100644
--- a/src/tools/clippy/clippy_utils/src/msrvs.rs
+++ b/src/tools/clippy/clippy_config/src/msrvs.rs
@@ -1,11 +1,8 @@
-use std::sync::OnceLock;
-
use rustc_ast::Attribute;
use rustc_semver::RustcVersion;
use rustc_session::Session;
-use rustc_span::Span;
-
-use crate::attrs::get_unique_attr;
+use rustc_span::{sym, Symbol};
+use serde::Deserialize;
macro_rules! msrv_aliases {
($($major:literal,$minor:literal,$patch:literal {
@@ -19,7 +16,7 @@ macro_rules! msrv_aliases {
// names may refer to stabilized feature flags or library items
msrv_aliases! {
- 1,71,0 { TUPLE_ARRAY_CONVERSIONS }
+ 1,71,0 { TUPLE_ARRAY_CONVERSIONS, BUILD_HASHER_HASH_ONE }
1,70,0 { OPTION_IS_SOME_AND, BINARY_HEAP_RETAIN }
1,68,0 { PATH_MAIN_SEPARATOR_STR }
1,65,0 { LET_ELSE, POINTER_CAST_CONSTNESS }
@@ -53,65 +50,45 @@ msrv_aliases! {
1,15,0 { MAYBE_BOUND_IN_WHERE }
}
-fn parse_msrv(msrv: &str, sess: Option<&Session>, span: Option<Span>) -> Option<RustcVersion> {
- if let Ok(version) = RustcVersion::parse(msrv) {
- return Some(version);
- } else if let Some(sess) = sess {
- if let Some(span) = span {
- sess.span_err(span, format!("`{msrv}` is not a valid Rust version"));
- }
- }
- None
-}
-
/// Tracks the current MSRV from `clippy.toml`, `Cargo.toml` or set via `#[clippy::msrv]`
-#[derive(Debug, Clone, Default)]
+#[derive(Debug, Clone)]
pub struct Msrv {
stack: Vec<RustcVersion>,
}
+impl<'de> Deserialize<'de> for Msrv {
+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+ where
+ D: serde::Deserializer<'de>,
+ {
+ let v = String::deserialize(deserializer)?;
+ RustcVersion::parse(&v)
+ .map(|v| Msrv { stack: vec![v] })
+ .map_err(|_| serde::de::Error::custom("not a valid Rust version"))
+ }
+}
+
impl Msrv {
- fn new(initial: Option<RustcVersion>) -> Self {
- Self {
- stack: Vec::from_iter(initial),
- }
+ pub fn empty() -> Msrv {
+ Msrv { stack: Vec::new() }
}
- fn read_inner(conf_msrv: &Option<String>, sess: &Session) -> Self {
+ pub fn read_cargo(&mut self, sess: &Session) {
let cargo_msrv = std::env::var("CARGO_PKG_RUST_VERSION")
.ok()
- .and_then(|v| parse_msrv(&v, None, None));
- let clippy_msrv = conf_msrv.as_ref().and_then(|s| {
- parse_msrv(s, None, None).or_else(|| {
- sess.err(format!(
- "error reading Clippy's configuration file. `{s}` is not a valid Rust version"
- ));
- None
- })
- });
-
- // if both files have an msrv, let's compare them and emit a warning if they differ
- if let Some(cargo_msrv) = cargo_msrv
- && let Some(clippy_msrv) = clippy_msrv
- && clippy_msrv != cargo_msrv
- {
- sess.warn(format!(
- "the MSRV in `clippy.toml` and `Cargo.toml` differ; using `{clippy_msrv}` from `clippy.toml`"
- ));
+ .and_then(|v| RustcVersion::parse(&v).ok());
+
+ match (self.current(), cargo_msrv) {
+ (None, Some(cargo_msrv)) => self.stack = vec![cargo_msrv],
+ (Some(clippy_msrv), Some(cargo_msrv)) => {
+ if clippy_msrv != cargo_msrv {
+ sess.warn(format!(
+ "the MSRV in `clippy.toml` and `Cargo.toml` differ; using `{clippy_msrv}` from `clippy.toml`"
+ ));
+ }
+ },
+ _ => {},
}
-
- Self::new(clippy_msrv.or(cargo_msrv))
- }
-
- /// Set the initial MSRV from the Clippy config file or from Cargo due to the `rust-version`
- /// field in `Cargo.toml`
- ///
- /// Returns a `&'static Msrv` as `Copy` types are more easily passed to the
- /// `register_{late,early}_pass` callbacks
- pub fn read(conf_msrv: &Option<String>, sess: &Session) -> &'static Self {
- static PARSED: OnceLock<Msrv> = OnceLock::new();
-
- PARSED.get_or_init(|| Self::read_inner(conf_msrv, sess))
}
pub fn current(&self) -> Option<RustcVersion> {
@@ -123,12 +100,25 @@ impl Msrv {
}
fn parse_attr(sess: &Session, attrs: &[Attribute]) -> Option<RustcVersion> {
- if let Some(msrv_attr) = get_unique_attr(sess, attrs, "msrv") {
- if let Some(msrv) = msrv_attr.value_str() {
- return parse_msrv(&msrv.to_string(), Some(sess), Some(msrv_attr.span));
+ let sym_msrv = Symbol::intern("msrv");
+ let mut msrv_attrs = attrs.iter().filter(|attr| attr.path_matches(&[sym::clippy, sym_msrv]));
+
+ if let Some(msrv_attr) = msrv_attrs.next() {
+ if let Some(duplicate) = msrv_attrs.last() {
+ sess.struct_span_err(duplicate.span, "`clippy::msrv` is defined multiple times")
+ .span_note(msrv_attr.span, "first definition found here")
+ .emit();
}
- sess.span_err(msrv_attr.span, "bad clippy attribute");
+ if let Some(msrv) = msrv_attr.value_str() {
+ if let Ok(version) = RustcVersion::parse(msrv.as_str()) {
+ return Some(version);
+ }
+
+ sess.span_err(msrv_attr.span, format!("`{msrv}` is not a valid Rust version"));
+ } else {
+ sess.span_err(msrv_attr.span, "bad clippy attribute");
+ }
}
None
diff --git a/src/tools/clippy/clippy_config/src/types.rs b/src/tools/clippy/clippy_config/src/types.rs
new file mode 100644
index 000000000..e898221ff
--- /dev/null
+++ b/src/tools/clippy/clippy_config/src/types.rs
@@ -0,0 +1,142 @@
+use serde::de::{self, Deserializer, Visitor};
+use serde::{ser, Deserialize, Serialize};
+use std::fmt;
+use std::hash::{Hash, Hasher};
+
+#[derive(Clone, Debug, Deserialize)]
+pub struct Rename {
+ pub path: String,
+ pub rename: String,
+}
+
+#[derive(Clone, Debug, Deserialize)]
+#[serde(untagged)]
+pub enum DisallowedPath {
+ Simple(String),
+ WithReason { path: String, reason: Option<String> },
+}
+
+impl DisallowedPath {
+ pub fn path(&self) -> &str {
+ let (Self::Simple(path) | Self::WithReason { path, .. }) = self;
+
+ path
+ }
+
+ pub fn reason(&self) -> Option<String> {
+ match self {
+ Self::WithReason {
+ reason: Some(reason), ..
+ } => Some(format!("{reason} (from clippy.toml)")),
+ _ => None,
+ }
+ }
+}
+
+#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Deserialize, Serialize)]
+pub enum MatchLintBehaviour {
+ AllTypes,
+ WellKnownTypes,
+ Never,
+}
+
+#[derive(Clone, Debug)]
+pub struct MacroMatcher {
+ pub name: String,
+ pub braces: (String, String),
+}
+
+impl Hash for MacroMatcher {
+ fn hash<H: Hasher>(&self, state: &mut H) {
+ self.name.hash(state);
+ }
+}
+
+impl PartialEq for MacroMatcher {
+ fn eq(&self, other: &Self) -> bool {
+ self.name == other.name
+ }
+}
+impl Eq for MacroMatcher {}
+
+impl<'de> Deserialize<'de> for MacroMatcher {
+ fn deserialize<D>(deser: D) -> Result<Self, D::Error>
+ where
+ D: Deserializer<'de>,
+ {
+ #[derive(Deserialize)]
+ #[serde(field_identifier, rename_all = "lowercase")]
+ enum Field {
+ Name,
+ Brace,
+ }
+ struct MacVisitor;
+ impl<'de> Visitor<'de> for MacVisitor {
+ type Value = MacroMatcher;
+
+ fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
+ formatter.write_str("struct MacroMatcher")
+ }
+
+ fn visit_map<V>(self, mut map: V) -> Result<Self::Value, V::Error>
+ where
+ V: de::MapAccess<'de>,
+ {
+ let mut name = None;
+ let mut brace: Option<String> = None;
+ while let Some(key) = map.next_key()? {
+ match key {
+ Field::Name => {
+ if name.is_some() {
+ return Err(de::Error::duplicate_field("name"));
+ }
+ name = Some(map.next_value()?);
+ },
+ Field::Brace => {
+ if brace.is_some() {
+ return Err(de::Error::duplicate_field("brace"));
+ }
+ brace = Some(map.next_value()?);
+ },
+ }
+ }
+ let name = name.ok_or_else(|| de::Error::missing_field("name"))?;
+ let brace = brace.ok_or_else(|| de::Error::missing_field("brace"))?;
+ Ok(MacroMatcher {
+ name,
+ braces: [("(", ")"), ("{", "}"), ("[", "]")]
+ .into_iter()
+ .find(|b| b.0 == brace)
+ .map(|(o, c)| (o.to_owned(), c.to_owned()))
+ .ok_or_else(|| de::Error::custom(format!("expected one of `(`, `{{`, `[` found `{brace}`")))?,
+ })
+ }
+ }
+
+ const FIELDS: &[&str] = &["name", "brace"];
+ deser.deserialize_struct("MacroMatcher", FIELDS, MacVisitor)
+ }
+}
+
+// these impls are never actually called but are used by the various config options that default to
+// empty lists
+macro_rules! unimplemented_serialize {
+ ($($t:ty,)*) => {
+ $(
+ impl Serialize for $t {
+ fn serialize<S>(&self, _serializer: S) -> Result<S::Ok, S::Error>
+ where
+ S: ser::Serializer,
+ {
+ Err(ser::Error::custom("unimplemented"))
+ }
+ }
+ )*
+ }
+}
+
+unimplemented_serialize! {
+ DisallowedPath,
+ Rename,
+ MacroMatcher,
+}
diff --git a/src/tools/clippy/clippy_dev/src/main.rs b/src/tools/clippy/clippy_dev/src/main.rs
index fca750faf..5bd9994e1 100644
--- a/src/tools/clippy/clippy_dev/src/main.rs
+++ b/src/tools/clippy/clippy_dev/src/main.rs
@@ -199,7 +199,6 @@ fn get_clap_config() -> ArgMatches {
"cargo",
"nursery",
"internal",
- "internal_warn",
]),
Arg::new("type").long("type").help("What directory the lint belongs in"),
Arg::new("msrv")
diff --git a/src/tools/clippy/clippy_dev/src/new_lint.rs b/src/tools/clippy/clippy_dev/src/new_lint.rs
index e64cf2c87..eeea53ce4 100644
--- a/src/tools/clippy/clippy_dev/src/new_lint.rs
+++ b/src/tools/clippy/clippy_dev/src/new_lint.rs
@@ -58,7 +58,7 @@ pub fn create(
};
create_lint(&lint, msrv).context("Unable to create lint implementation")?;
- create_test(&lint).context("Unable to create a test for the new lint")?;
+ create_test(&lint, msrv).context("Unable to create a test for the new lint")?;
if lint.ty.is_none() {
add_lint(&lint, msrv).context("Unable to add lint to clippy_lints/src/lib.rs")?;
@@ -88,15 +88,21 @@ fn create_lint(lint: &LintData<'_>, enable_msrv: bool) -> io::Result<()> {
}
}
-fn create_test(lint: &LintData<'_>) -> io::Result<()> {
- fn create_project_layout<P: Into<PathBuf>>(lint_name: &str, location: P, case: &str, hint: &str) -> io::Result<()> {
+fn create_test(lint: &LintData<'_>, msrv: bool) -> io::Result<()> {
+ fn create_project_layout<P: Into<PathBuf>>(
+ lint_name: &str,
+ location: P,
+ case: &str,
+ hint: &str,
+ msrv: bool,
+ ) -> io::Result<()> {
let mut path = location.into().join(case);
fs::create_dir(&path)?;
write_file(path.join("Cargo.toml"), get_manifest_contents(lint_name, hint))?;
path.push("src");
fs::create_dir(&path)?;
- write_file(path.join("main.rs"), get_test_file_contents(lint_name))?;
+ write_file(path.join("main.rs"), get_test_file_contents(lint_name, msrv))?;
Ok(())
}
@@ -106,13 +112,25 @@ fn create_test(lint: &LintData<'_>) -> io::Result<()> {
let test_dir = lint.project_root.join(&relative_test_dir);
fs::create_dir(&test_dir)?;
- create_project_layout(lint.name, &test_dir, "fail", "Content that triggers the lint goes here")?;
- create_project_layout(lint.name, &test_dir, "pass", "This file should not trigger the lint")?;
+ create_project_layout(
+ lint.name,
+ &test_dir,
+ "fail",
+ "Content that triggers the lint goes here",
+ msrv,
+ )?;
+ create_project_layout(
+ lint.name,
+ &test_dir,
+ "pass",
+ "This file should not trigger the lint",
+ false,
+ )?;
println!("Generated test directories: `{relative_test_dir}/pass`, `{relative_test_dir}/fail`");
} else {
let test_path = format!("tests/ui/{}.rs", lint.name);
- let test_contents = get_test_file_contents(lint.name);
+ let test_contents = get_test_file_contents(lint.name, msrv);
write_file(lint.project_root.join(&test_path), test_contents)?;
println!("Generated test file: `{test_path}`");
@@ -194,8 +212,8 @@ pub(crate) fn get_stabilization_version() -> String {
parse_manifest(&contents).expect("Unable to find package version in `Cargo.toml`")
}
-fn get_test_file_contents(lint_name: &str) -> String {
- formatdoc!(
+fn get_test_file_contents(lint_name: &str, msrv: bool) -> String {
+ let mut test = formatdoc!(
r#"
#![warn(clippy::{lint_name})]
@@ -203,7 +221,29 @@ fn get_test_file_contents(lint_name: &str) -> String {
// test code goes here
}}
"#
- )
+ );
+
+ if msrv {
+ let _ = writedoc!(
+ test,
+ r#"
+
+ // TODO: set xx to the version one below the MSRV used by the lint, and yy to
+ // the version used by the lint
+ #[clippy::msrv = "1.xx"]
+ fn msrv_1_xx() {{
+ // a simple example that would trigger the lint if the MSRV were met
+ }}
+
+ #[clippy::msrv = "1.yy"]
+ fn msrv_1_yy() {{
+ // the same example as above
+ }}
+ "#
+ );
+ }
+
+ test
}
fn get_manifest_contents(lint_name: &str, hint: &str) -> String {
@@ -258,7 +298,7 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String {
)
});
- let _: fmt::Result = write!(result, "{}", get_lint_declaration(&name_upper, category));
+ let _: fmt::Result = writeln!(result, "{}", get_lint_declaration(&name_upper, category));
result.push_str(&if enable_msrv {
formatdoc!(
@@ -281,7 +321,6 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String {
}}
// TODO: Add MSRV level to `clippy_utils/src/msrvs.rs` if needed.
- // TODO: Add MSRV test to `tests/ui/min_rust_version_attr.rs`.
// TODO: Update msrv config comment in `clippy_lints/src/utils/conf.rs`
"#
)
@@ -307,11 +346,11 @@ fn get_lint_declaration(name_upper: &str, category: &str) -> String {
/// ### Why is this bad?
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// // example code where clippy issues a warning
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// // example code which does not raise clippy warning
/// ```
#[clippy::version = "{}"]
diff --git a/src/tools/clippy/clippy_dev/src/update_lints.rs b/src/tools/clippy/clippy_dev/src/update_lints.rs
index 842aeed2a..6b76a44de 100644
--- a/src/tools/clippy/clippy_dev/src/update_lints.rs
+++ b/src/tools/clippy/clippy_dev/src/update_lints.rs
@@ -588,7 +588,7 @@ impl Lint {
.collect()
}
- /// Returns all internal lints (not `internal_warn` lints)
+ /// Returns all internal lints
#[must_use]
fn internal_lints(lints: &[Self]) -> Vec<Self> {
lints.iter().filter(|l| l.group == "internal").cloned().collect()
diff --git a/src/tools/clippy/clippy_lints/Cargo.toml b/src/tools/clippy/clippy_lints/Cargo.toml
index dcd9a4adc..4bc27fd48 100644
--- a/src/tools/clippy/clippy_lints/Cargo.toml
+++ b/src/tools/clippy/clippy_lints/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "clippy_lints"
-version = "0.1.74"
+version = "0.1.75"
description = "A bunch of helpful lints to avoid common pitfalls in Rust"
repository = "https://github.com/rust-lang/rust-clippy"
readme = "README.md"
@@ -11,6 +11,7 @@ edition = "2021"
[dependencies]
arrayvec = { version = "0.7", default-features = false }
cargo_metadata = "0.15.3"
+clippy_config = { path = "../clippy_config" }
clippy_utils = { path = "../clippy_utils" }
declare_clippy_lint = { path = "../declare_clippy_lint" }
if_chain = "1.0"
@@ -28,10 +29,13 @@ semver = "1.0"
rustc-semver = "1.1"
url = "2.2"
+[dev-dependencies]
+walkdir = "2.3"
+
[features]
-deny-warnings = ["clippy_utils/deny-warnings"]
+deny-warnings = ["clippy_config/deny-warnings", "clippy_utils/deny-warnings"]
# build clippy with internal lints enabled, off by default
-internal = ["clippy_utils/internal", "serde_json", "tempfile", "regex"]
+internal = ["serde_json", "tempfile", "regex"]
[package.metadata.rust-analyzer]
# This crate uses #[feature(rustc_private)]
diff --git a/src/tools/clippy/clippy_lints/src/absolute_paths.rs b/src/tools/clippy/clippy_lints/src/absolute_paths.rs
index 04417c4c4..582423603 100644
--- a/src/tools/clippy/clippy_lints/src/absolute_paths.rs
+++ b/src/tools/clippy/clippy_lints/src/absolute_paths.rs
@@ -24,11 +24,11 @@ declare_clippy_lint! {
/// using absolute paths is the proper way of referencing items in one.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let x = std::f64::consts::PI;
/// ```
/// Use any of the below instead, or anything else:
- /// ```rust
+ /// ```no_run
/// use std::f64;
/// use std::f64::consts;
/// use std::f64::consts::PI;
diff --git a/src/tools/clippy/clippy_lints/src/allow_attributes.rs b/src/tools/clippy/clippy_lints/src/allow_attributes.rs
index e1ef514ed..e3f4cf79d 100644
--- a/src/tools/clippy/clippy_lints/src/allow_attributes.rs
+++ b/src/tools/clippy/clippy_lints/src/allow_attributes.rs
@@ -8,6 +8,7 @@ use rustc_middle::lint::in_external_macro;
use rustc_session::{declare_lint_pass, declare_tool_lint};
declare_clippy_lint! {
+ /// ### What it does
/// Checks for usage of the `#[allow]` attribute and suggests replacing it with
/// the `#[expect]` (See [RFC 2383](https://rust-lang.github.io/rfcs/2383-lint-reasons.html))
///
@@ -19,7 +20,6 @@ declare_clippy_lint! {
/// (`#![allow]`) are usually used to enable or disable lints on a global scale.
///
/// ### Why is this bad?
- ///
/// `#[expect]` attributes suppress the lint emission, but emit a warning, if
/// the expectation is unfulfilled. This can be useful to be notified when the
/// lint is no longer triggered.
diff --git a/src/tools/clippy/clippy_lints/src/almost_complete_range.rs b/src/tools/clippy/clippy_lints/src/almost_complete_range.rs
index 32d80f42e..e85878eb5 100644
--- a/src/tools/clippy/clippy_lints/src/almost_complete_range.rs
+++ b/src/tools/clippy/clippy_lints/src/almost_complete_range.rs
@@ -1,5 +1,5 @@
+use clippy_config::msrvs::{self, Msrv};
use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::source::{trim_span, walk_span_to_context};
use rustc_ast::ast::{Expr, ExprKind, LitKind, Pat, PatKind, RangeEnd, RangeLimits};
use rustc_errors::Applicability;
@@ -17,11 +17,11 @@ declare_clippy_lint! {
/// This (`'a'..'z'`) is almost certainly a typo meant to include all letters.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let _ = 'a'..'z';
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// let _ = 'a'..='z';
/// ```
#[clippy::version = "1.68.0"]
@@ -82,33 +82,20 @@ fn check_range(cx: &EarlyContext<'_>, span: Span, start: &Expr, end: &Expr, sugg
(
Ok(LitKind::Byte(b'a') | LitKind::Char('a')),
Ok(LitKind::Byte(b'z') | LitKind::Char('z'))
- )
- | (
+ ) | (
Ok(LitKind::Byte(b'A') | LitKind::Char('A')),
Ok(LitKind::Byte(b'Z') | LitKind::Char('Z')),
- )
- | (
+ ) | (
Ok(LitKind::Byte(b'0') | LitKind::Char('0')),
Ok(LitKind::Byte(b'9') | LitKind::Char('9')),
)
)
&& !in_external_macro(cx.sess(), span)
{
- span_lint_and_then(
- cx,
- ALMOST_COMPLETE_RANGE,
- span,
- "almost complete ascii range",
- |diag| {
- if let Some((span, sugg)) = sugg {
- diag.span_suggestion(
- span,
- "use an inclusive range",
- sugg,
- Applicability::MaybeIncorrect,
- );
- }
+ span_lint_and_then(cx, ALMOST_COMPLETE_RANGE, span, "almost complete ascii range", |diag| {
+ if let Some((span, sugg)) = sugg {
+ diag.span_suggestion(span, "use an inclusive range", sugg, Applicability::MaybeIncorrect);
}
- );
+ });
}
}
diff --git a/src/tools/clippy/clippy_lints/src/approx_const.rs b/src/tools/clippy/clippy_lints/src/approx_const.rs
index ccf82f132..b4f778f12 100644
--- a/src/tools/clippy/clippy_lints/src/approx_const.rs
+++ b/src/tools/clippy/clippy_lints/src/approx_const.rs
@@ -1,5 +1,5 @@
+use clippy_config::msrvs::{self, Msrv};
use clippy_utils::diagnostics::span_lint_and_help;
-use clippy_utils::msrvs::{self, Msrv};
use rustc_ast::ast::{FloatTy, LitFloatType, LitKind};
use rustc_hir::{Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
@@ -24,12 +24,12 @@ declare_clippy_lint! {
/// issue](https://github.com/rust-lang/rust/issues).
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let x = 3.14;
/// let y = 1_f64 / x;
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// let x = std::f32::consts::PI;
/// let y = std::f64::consts::FRAC_1_PI;
/// ```
diff --git a/src/tools/clippy/clippy_lints/src/arc_with_non_send_sync.rs b/src/tools/clippy/clippy_lints/src/arc_with_non_send_sync.rs
index 35a04b5e4..192bc7d9d 100644
--- a/src/tools/clippy/clippy_lints/src/arc_with_non_send_sync.rs
+++ b/src/tools/clippy/clippy_lints/src/arc_with_non_send_sync.rs
@@ -18,7 +18,7 @@ declare_clippy_lint! {
/// either `T` should be made `Send + Sync` or an `Rc` should be used instead of an `Arc`
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # use std::cell::RefCell;
/// # use std::sync::Arc;
///
@@ -62,19 +62,21 @@ impl<'tcx> LateLintPass<'tcx> for ArcWithNonSendSync {
ARC_WITH_NON_SEND_SYNC,
expr.span,
"usage of an `Arc` that is not `Send` or `Sync`",
- |diag| with_forced_trimmed_paths!({
- if !is_send {
- diag.note(format!("the trait `Send` is not implemented for `{arg_ty}`"));
- }
- if !is_sync {
- diag.note(format!("the trait `Sync` is not implemented for `{arg_ty}`"));
- }
+ |diag| {
+ with_forced_trimmed_paths!({
+ if !is_send {
+ diag.note(format!("the trait `Send` is not implemented for `{arg_ty}`"));
+ }
+ if !is_sync {
+ diag.note(format!("the trait `Sync` is not implemented for `{arg_ty}`"));
+ }
- diag.note(format!("required for `{ty}` to implement `Send` and `Sync`"));
+ diag.note(format!("required for `{ty}` to implement `Send` and `Sync`"));
- diag.help("consider using an `Rc` instead or wrapping the inner type with a `Mutex`");
- }
- ));
+ diag.help("consider using an `Rc` instead or wrapping the inner type with a `Mutex`");
+ });
+ },
+ );
}
}
}
diff --git a/src/tools/clippy/clippy_lints/src/as_conversions.rs b/src/tools/clippy/clippy_lints/src/as_conversions.rs
index b9dda49ca..2de205d80 100644
--- a/src/tools/clippy/clippy_lints/src/as_conversions.rs
+++ b/src/tools/clippy/clippy_lints/src/as_conversions.rs
@@ -48,11 +48,10 @@ declare_lint_pass!(AsConversions => [AS_CONVERSIONS]);
impl<'tcx> LateLintPass<'tcx> for AsConversions {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
- if in_external_macro(cx.sess(), expr.span) || is_from_proc_macro(cx, expr) {
- return;
- }
-
- if let ExprKind::Cast(_, _) = expr.kind {
+ if let ExprKind::Cast(_, _) = expr.kind
+ && !in_external_macro(cx.sess(), expr.span)
+ && !is_from_proc_macro(cx, expr)
+ {
span_lint_and_help(
cx,
AS_CONVERSIONS,
diff --git a/src/tools/clippy/clippy_lints/src/assertions_on_result_states.rs b/src/tools/clippy/clippy_lints/src/assertions_on_result_states.rs
index 2980c9d6d..71ec87a88 100644
--- a/src/tools/clippy/clippy_lints/src/assertions_on_result_states.rs
+++ b/src/tools/clippy/clippy_lints/src/assertions_on_result_states.rs
@@ -58,7 +58,7 @@ impl<'tcx> LateLintPass<'tcx> for AssertionsOnResultStates {
return;
}
}
- let semicolon = if is_expr_final_block_expr(cx.tcx, e) {";"} else {""};
+ let semicolon = if is_expr_final_block_expr(cx.tcx, e) { ";" } else { "" };
let mut app = Applicability::MachineApplicable;
match method_segment.ident.as_str() {
"is_ok" if type_suitable_to_unwrap(cx, args.type_at(1)) => {
@@ -74,7 +74,7 @@ impl<'tcx> LateLintPass<'tcx> for AssertionsOnResultStates {
),
app,
);
- }
+ },
"is_err" if type_suitable_to_unwrap(cx, args.type_at(0)) => {
span_lint_and_sugg(
cx,
@@ -88,7 +88,7 @@ impl<'tcx> LateLintPass<'tcx> for AssertionsOnResultStates {
),
app,
);
- }
+ },
_ => (),
};
}
diff --git a/src/tools/clippy/clippy_lints/src/async_yields_async.rs b/src/tools/clippy/clippy_lints/src/async_yields_async.rs
index 9464694a3..ec2447dae 100644
--- a/src/tools/clippy/clippy_lints/src/async_yields_async.rs
+++ b/src/tools/clippy/clippy_lints/src/async_yields_async.rs
@@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_hir_and_then;
use clippy_utils::source::snippet;
use clippy_utils::ty::implements_trait;
use rustc_errors::Applicability;
-use rustc_hir::{AsyncGeneratorKind, Body, BodyId, ExprKind, GeneratorKind, QPath};
+use rustc_hir::{Body, BodyId, CoroutineKind, CoroutineSource, ExprKind, QPath};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
@@ -15,7 +15,7 @@ declare_clippy_lint! {
/// An await is likely missing.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// async fn foo() {}
///
/// fn bar() {
@@ -26,7 +26,7 @@ declare_clippy_lint! {
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// async fn foo() {}
///
/// fn bar() {
@@ -45,10 +45,10 @@ declare_lint_pass!(AsyncYieldsAsync => [ASYNC_YIELDS_ASYNC]);
impl<'tcx> LateLintPass<'tcx> for AsyncYieldsAsync {
fn check_body(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'_>) {
- use AsyncGeneratorKind::{Block, Closure};
+ use CoroutineSource::{Block, Closure};
// For functions, with explicitly defined types, don't warn.
// XXXkhuey maybe we should?
- if let Some(GeneratorKind::Async(Block | Closure)) = body.generator_kind {
+ if let Some(CoroutineKind::Async(Block | Closure)) = body.coroutine_kind {
if let Some(future_trait_def_id) = cx.tcx.lang_items().future_trait() {
let body_id = BodyId {
hir_id: body.value.hir_id,
diff --git a/src/tools/clippy/clippy_lints/src/attrs.rs b/src/tools/clippy/clippy_lints/src/attrs.rs
index 0546807ba..38364af27 100644
--- a/src/tools/clippy/clippy_lints/src/attrs.rs
+++ b/src/tools/clippy/clippy_lints/src/attrs.rs
@@ -1,9 +1,9 @@
//! checks for attributes
+use clippy_config::msrvs::{self, Msrv};
use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then};
use clippy_utils::is_from_proc_macro;
use clippy_utils::macros::{is_panic, macro_backtrace};
-use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::source::{first_line_of_span, is_present_in_source, snippet_opt, without_block_comments};
use if_chain::if_chain;
use rustc_ast::token::{Token, TokenKind};
@@ -19,9 +19,8 @@ use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, Level,
use rustc_middle::lint::in_external_macro;
use rustc_middle::ty;
use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass};
-use rustc_span::source_map::Span;
use rustc_span::symbol::Symbol;
-use rustc_span::{sym, DUMMY_SP};
+use rustc_span::{sym, DUMMY_SP, Span};
use semver::Version;
static UNIX_SYSTEMS: &[&str] = &[
@@ -129,7 +128,7 @@ declare_clippy_lint! {
/// a valid semver. Failing that, the contained information is useless.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// #[deprecated(since = "forever")]
/// fn something_else() { /* ... */ }
/// ```
@@ -156,14 +155,14 @@ declare_clippy_lint! {
/// currently works for basic cases but is not perfect.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// #[allow(dead_code)]
///
/// fn not_quite_good_code() { }
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// // Good (as inner attribute)
/// #![allow(dead_code)]
///
@@ -183,7 +182,7 @@ declare_clippy_lint! {
declare_clippy_lint! {
/// ### What it does
- /// Checks for empty lines after documenation comments.
+ /// Checks for empty lines after documentation comments.
///
/// ### Why is this bad?
/// The documentation comment was most likely meant to be an inner attribute or regular comment.
@@ -198,25 +197,25 @@ declare_clippy_lint! {
/// Does not detect empty lines after doc attributes (e.g. `#[doc = ""]`).
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// /// Some doc comment with a blank line after it.
///
/// fn not_quite_good_code() { }
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// /// Good (no blank line)
/// fn this_is_fine() { }
/// ```
///
- /// ```rust
+ /// ```no_run
/// // Good (convert to a regular comment)
///
/// fn this_is_fine_too() { }
/// ```
///
- /// ```rust
+ /// ```no_run
/// //! Good (convert to a comment on an inner attribute)
///
/// fn this_is_fine_as_well() { }
@@ -236,12 +235,12 @@ declare_clippy_lint! {
/// These lints should only be enabled on a lint-by-lint basis and with careful consideration.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// #![deny(clippy::restriction)]
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// #![deny(clippy::as_conversions)]
/// ```
#[clippy::version = "1.47.0"]
@@ -265,13 +264,13 @@ declare_clippy_lint! {
/// [#3123](https://github.com/rust-lang/rust-clippy/pull/3123#issuecomment-422321765)
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// #[cfg_attr(rustfmt, rustfmt_skip)]
/// fn main() { }
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// #[rustfmt::skip]
/// fn main() { }
/// ```
@@ -290,13 +289,13 @@ declare_clippy_lint! {
/// by the conditional compilation engine.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// #[cfg(linux)]
/// fn conditional() { }
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # mod hidden {
/// #[cfg(target_os = "linux")]
/// fn conditional() { }
@@ -325,14 +324,14 @@ declare_clippy_lint! {
/// ensure that others understand the reasoning
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// #![feature(lint_reasons)]
///
/// #![allow(clippy::some_lint)]
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// #![feature(lint_reasons)]
///
/// #![allow(clippy::some_lint, reason = "False positive rust-lang/rust-clippy#1002020")]
@@ -352,7 +351,7 @@ declare_clippy_lint! {
/// panicking with the expected message, and not another unrelated panic.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// fn random() -> i32 { 0 }
///
/// #[should_panic]
@@ -363,7 +362,7 @@ declare_clippy_lint! {
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// fn random() -> i32 { 0 }
///
/// #[should_panic = "attempt to divide by zero"]
@@ -386,13 +385,13 @@ declare_clippy_lint! {
/// If there is only one condition, no need to wrap it into `any` or `all` combinators.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// #[cfg(any(unix))]
/// pub struct Bar;
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// #[cfg(unix)]
/// pub struct Bar;
/// ```
@@ -409,16 +408,16 @@ declare_clippy_lint! {
///
/// ### Why is this bad?
/// Misspelling `feature` as `features` can be sometimes hard to spot. It
- /// may cause conditional compilation not work quitely.
+ /// may cause conditional compilation not work quietly.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// #[cfg(features = "some-feature")]
/// fn conditional() { }
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// #[cfg(feature = "some-feature")]
/// fn conditional() { }
/// ```
@@ -602,9 +601,26 @@ fn check_should_panic_reason(cx: &LateContext<'_>, attr: &Attribute) {
if let AttrArgs::Delimited(args) = &normal_attr.item.args
&& let mut tt_iter = args.tokens.trees()
- && let Some(TokenTree::Token(Token { kind: TokenKind::Ident(sym::expected, _), .. }, _)) = tt_iter.next()
- && let Some(TokenTree::Token(Token { kind: TokenKind::Eq, .. }, _)) = tt_iter.next()
- && let Some(TokenTree::Token(Token { kind: TokenKind::Literal(_), .. }, _)) = tt_iter.next()
+ && let Some(TokenTree::Token(
+ Token {
+ kind: TokenKind::Ident(sym::expected, _),
+ ..
+ },
+ _,
+ )) = tt_iter.next()
+ && let Some(TokenTree::Token(
+ Token {
+ kind: TokenKind::Eq, ..
+ },
+ _,
+ )) = tt_iter.next()
+ && let Some(TokenTree::Token(
+ Token {
+ kind: TokenKind::Literal(_),
+ ..
+ },
+ _,
+ )) = tt_iter.next()
{
// `#[should_panic(expected = "..")]` found, good
return;
@@ -795,7 +811,7 @@ impl EarlyLintPass for EarlyAttributes {
/// Check for empty lines after outer attributes.
///
-/// Attributes and documenation comments are both considered outer attributes
+/// Attributes and documentation comments are both considered outer attributes
/// by the AST. However, the average user likely considers them to be different.
/// Checking for empty lines after each of these attributes is split into two different
/// lints but can share the same logic.
@@ -914,7 +930,9 @@ fn check_nested_cfg(cx: &EarlyContext<'_>, items: &[NestedMetaItem]) {
fn check_nested_misused_cfg(cx: &EarlyContext<'_>, items: &[NestedMetaItem]) {
for item in items {
if let NestedMetaItem::MetaItem(meta) = item {
- if meta.has_name(sym!(features)) && let Some(val) = meta.value_str() {
+ if meta.has_name(sym!(features))
+ && let Some(val) = meta.value_str()
+ {
span_lint_and_sugg(
cx,
MAYBE_MISUSED_CFG,
@@ -933,16 +951,16 @@ fn check_nested_misused_cfg(cx: &EarlyContext<'_>, items: &[NestedMetaItem]) {
}
fn check_minimal_cfg_condition(cx: &EarlyContext<'_>, attr: &Attribute) {
- if attr.has_name(sym::cfg) &&
- let Some(items) = attr.meta_item_list()
+ if attr.has_name(sym::cfg)
+ && let Some(items) = attr.meta_item_list()
{
check_nested_cfg(cx, &items);
}
}
fn check_misused_cfg(cx: &EarlyContext<'_>, attr: &Attribute) {
- if attr.has_name(sym::cfg) &&
- let Some(items) = attr.meta_item_list()
+ if attr.has_name(sym::cfg)
+ && let Some(items) = attr.meta_item_list()
{
check_nested_misused_cfg(cx, &items);
}
diff --git a/src/tools/clippy/clippy_lints/src/await_holding_invalid.rs b/src/tools/clippy/clippy_lints/src/await_holding_invalid.rs
index 7dd808a7b..06b74b972 100644
--- a/src/tools/clippy/clippy_lints/src/await_holding_invalid.rs
+++ b/src/tools/clippy/clippy_lints/src/await_holding_invalid.rs
@@ -1,15 +1,14 @@
+use clippy_config::types::DisallowedPath;
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::{match_def_path, paths};
use rustc_data_structures::fx::FxHashMap;
use rustc_hir::def_id::DefId;
-use rustc_hir::{AsyncGeneratorKind, Body, GeneratorKind};
+use rustc_hir::{Body, CoroutineKind, CoroutineSource};
use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::mir::GeneratorLayout;
+use rustc_middle::mir::CoroutineLayout;
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::{sym, Span};
-use crate::utils::conf::DisallowedPath;
-
declare_clippy_lint! {
/// ### What it does
/// Checks for calls to await while holding a non-async-aware MutexGuard.
@@ -29,7 +28,7 @@ declare_clippy_lint! {
/// to wrap the `.lock()` call in a block instead of explicitly dropping the guard.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # use std::sync::Mutex;
/// # async fn baz() {}
/// async fn foo(x: &Mutex<u32>) {
@@ -47,7 +46,7 @@ declare_clippy_lint! {
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # use std::sync::Mutex;
/// # async fn baz() {}
/// async fn foo(x: &Mutex<u32>) {
@@ -87,7 +86,7 @@ declare_clippy_lint! {
/// to wrap the `.borrow[_mut]()` call in a block instead of explicitly dropping the ref.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # use std::cell::RefCell;
/// # async fn baz() {}
/// async fn foo(x: &RefCell<u32>) {
@@ -105,7 +104,7 @@ declare_clippy_lint! {
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # use std::cell::RefCell;
/// # async fn baz() {}
/// async fn foo(x: &RefCell<u32>) {
@@ -151,7 +150,7 @@ declare_clippy_lint! {
/// ]
/// ```
///
- /// ```rust
+ /// ```no_run
/// # async fn baz() {}
/// struct CustomLockType;
/// struct OtherCustomLockType;
@@ -195,26 +194,26 @@ impl LateLintPass<'_> for AwaitHolding {
}
fn check_body(&mut self, cx: &LateContext<'_>, body: &'_ Body<'_>) {
- use AsyncGeneratorKind::{Block, Closure, Fn};
- if let Some(GeneratorKind::Async(Block | Closure | Fn)) = body.generator_kind {
+ use CoroutineSource::{Block, Closure, Fn};
+ if let Some(CoroutineKind::Async(Block | Closure | Fn)) = body.coroutine_kind {
let def_id = cx.tcx.hir().body_owner_def_id(body.id());
- if let Some(generator_layout) = cx.tcx.mir_generator_witnesses(def_id) {
- self.check_interior_types(cx, generator_layout);
+ if let Some(coroutine_layout) = cx.tcx.mir_coroutine_witnesses(def_id) {
+ self.check_interior_types(cx, coroutine_layout);
}
}
}
}
impl AwaitHolding {
- fn check_interior_types(&self, cx: &LateContext<'_>, generator: &GeneratorLayout<'_>) {
- for (ty_index, ty_cause) in generator.field_tys.iter_enumerated() {
+ fn check_interior_types(&self, cx: &LateContext<'_>, coroutine: &CoroutineLayout<'_>) {
+ for (ty_index, ty_cause) in coroutine.field_tys.iter_enumerated() {
if let rustc_middle::ty::Adt(adt, _) = ty_cause.ty.kind() {
let await_points = || {
- generator
+ coroutine
.variant_source_info
.iter_enumerated()
.filter_map(|(variant, source_info)| {
- generator.variant_fields[variant]
+ coroutine.variant_fields[variant]
.raw
.contains(&ty_index)
.then_some(source_info.span)
@@ -287,5 +286,8 @@ fn is_mutex_guard(cx: &LateContext<'_>, def_id: DefId) -> bool {
}
fn is_refcell_ref(cx: &LateContext<'_>, def_id: DefId) -> bool {
- match_def_path(cx, def_id, &paths::REFCELL_REF) || match_def_path(cx, def_id, &paths::REFCELL_REFMUT)
+ matches!(
+ cx.tcx.get_diagnostic_name(def_id),
+ Some(sym::RefCellRef | sym::RefCellRefMut)
+ )
}
diff --git a/src/tools/clippy/clippy_lints/src/blocks_in_if_conditions.rs b/src/tools/clippy/clippy_lints/src/blocks_in_if_conditions.rs
index 1593d7b0f..04bf541a5 100644
--- a/src/tools/clippy/clippy_lints/src/blocks_in_if_conditions.rs
+++ b/src/tools/clippy/clippy_lints/src/blocks_in_if_conditions.rs
@@ -21,7 +21,7 @@ declare_clippy_lint! {
/// Style, using blocks in the condition makes it hard to read.
///
/// ### Examples
- /// ```rust
+ /// ```no_run
/// # fn somefunc() -> bool { true };
/// if { true } { /* ... */ }
///
@@ -29,7 +29,7 @@ declare_clippy_lint! {
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # fn somefunc() -> bool { true };
/// if true { /* ... */ }
///
diff --git a/src/tools/clippy/clippy_lints/src/bool_assert_comparison.rs b/src/tools/clippy/clippy_lints/src/bool_assert_comparison.rs
index 450359771..665dbd6f7 100644
--- a/src/tools/clippy/clippy_lints/src/bool_assert_comparison.rs
+++ b/src/tools/clippy/clippy_lints/src/bool_assert_comparison.rs
@@ -18,13 +18,13 @@ declare_clippy_lint! {
/// It is shorter to use the equivalent.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// assert_eq!("a".is_empty(), false);
/// assert_ne!("a".is_empty(), true);
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// assert!(!"a".is_empty());
/// ```
#[clippy::version = "1.53.0"]
diff --git a/src/tools/clippy/clippy_lints/src/bool_to_int_with_if.rs b/src/tools/clippy/clippy_lints/src/bool_to_int_with_if.rs
index 1828dd651..156cb34df 100644
--- a/src/tools/clippy/clippy_lints/src/bool_to_int_with_if.rs
+++ b/src/tools/clippy/clippy_lints/src/bool_to_int_with_if.rs
@@ -21,7 +21,7 @@ declare_clippy_lint! {
/// See https://doc.rust-lang.org/std/primitive.bool.html#impl-From%3Cbool%3E
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # let condition = false;
/// if condition {
/// 1_i64
@@ -30,12 +30,12 @@ declare_clippy_lint! {
/// };
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # let condition = false;
/// i64::from(condition);
/// ```
/// or
- /// ```rust
+ /// ```no_run
/// # let condition = false;
/// condition as i64;
/// ```
@@ -55,7 +55,11 @@ impl<'tcx> LateLintPass<'tcx> for BoolToIntWithIf {
}
fn check_if_else<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx rustc_hir::Expr<'tcx>) {
- if let Some(If { cond, then, r#else: Some(r#else) }) = If::hir(expr)
+ if let Some(If {
+ cond,
+ then,
+ r#else: Some(r#else),
+ }) = If::hir(expr)
&& let Some(then_lit) = int_literal(then)
&& let Some(else_lit) = int_literal(r#else)
{
@@ -90,19 +94,18 @@ fn check_if_else<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx rustc_hir::Expr<'tcx>
let into_snippet = snippet.clone().maybe_par();
let as_snippet = snippet.as_ty(ty);
- span_lint_and_then(cx,
+ span_lint_and_then(
+ cx,
BOOL_TO_INT_WITH_IF,
expr.span,
"boolean to int conversion using if",
|diag| {
- diag.span_suggestion(
- expr.span,
- "replace with from",
- suggestion,
- applicability,
- );
- diag.note(format!("`{as_snippet}` or `{into_snippet}.into()` can also be valid options"));
- });
+ diag.span_suggestion(expr.span, "replace with from", suggestion, applicability);
+ diag.note(format!(
+ "`{as_snippet}` or `{into_snippet}.into()` can also be valid options"
+ ));
+ },
+ );
};
}
@@ -110,7 +113,7 @@ fn check_if_else<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx rustc_hir::Expr<'tcx>
fn int_literal<'tcx>(expr: &'tcx rustc_hir::Expr<'tcx>) -> Option<&'tcx rustc_hir::Expr<'tcx>> {
if let ExprKind::Block(block, _) = expr.kind
&& let Block {
- stmts: [], // Shouldn't lint if statements with side effects
+ stmts: [], // Shouldn't lint if statements with side effects
expr: Some(expr),
..
} = block
diff --git a/src/tools/clippy/clippy_lints/src/booleans.rs b/src/tools/clippy/clippy_lints/src/booleans.rs
index 04cca9e31..0e0d229e8 100644
--- a/src/tools/clippy/clippy_lints/src/booleans.rs
+++ b/src/tools/clippy/clippy_lints/src/booleans.rs
@@ -10,8 +10,7 @@ use rustc_hir::{BinOpKind, Body, Expr, ExprKind, FnDecl, UnOp};
use rustc_lint::{LateContext, LateLintPass, Level};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::def_id::LocalDefId;
-use rustc_span::source_map::Span;
-use rustc_span::sym;
+use rustc_span::{sym, Span};
declare_clippy_lint! {
/// ### What it does
@@ -472,8 +471,9 @@ impl<'a, 'tcx> Visitor<'tcx> for NonminimalBoolVisitor<'a, 'tcx> {
self.bool_expr(e);
},
ExprKind::Unary(UnOp::Not, inner) => {
- if let ExprKind::Unary(UnOp::Not, ex) = inner.kind &&
- !self.cx.typeck_results().node_types()[ex.hir_id].is_bool() {
+ if let ExprKind::Unary(UnOp::Not, ex) = inner.kind
+ && !self.cx.typeck_results().node_types()[ex.hir_id].is_bool()
+ {
return;
}
if self.cx.typeck_results().node_types()[inner.hir_id].is_bool() {
@@ -500,10 +500,10 @@ struct NotSimplificationVisitor<'a, 'tcx> {
impl<'a, 'tcx> Visitor<'tcx> for NotSimplificationVisitor<'a, 'tcx> {
fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
- if let ExprKind::Unary(UnOp::Not, inner) = &expr.kind &&
- !inner.span.from_expansion() &&
- let Some(suggestion) = simplify_not(self.cx, inner)
- && self.cx.tcx.lint_level_at_node(NONMINIMAL_BOOL, expr.hir_id).0 != Level::Allow
+ if let ExprKind::Unary(UnOp::Not, inner) = &expr.kind
+ && !inner.span.from_expansion()
+ && let Some(suggestion) = simplify_not(self.cx, inner)
+ && self.cx.tcx.lint_level_at_node(NONMINIMAL_BOOL, expr.hir_id).0 != Level::Allow
{
span_lint_and_sugg(
self.cx,
diff --git a/src/tools/clippy/clippy_lints/src/borrow_deref_ref.rs b/src/tools/clippy/clippy_lints/src/borrow_deref_ref.rs
index b3dbbb08f..a38269083 100644
--- a/src/tools/clippy/clippy_lints/src/borrow_deref_ref.rs
+++ b/src/tools/clippy/clippy_lints/src/borrow_deref_ref.rs
@@ -19,7 +19,7 @@ declare_clippy_lint! {
///
/// ### Known problems
/// False negative on such code:
- /// ```
+ /// ```no_run
/// let x = &12;
/// let addr_x = &x as *const _ as usize;
/// let addr_y = &&*x as *const _ as usize; // assert ok now, and lint triggered.
@@ -28,14 +28,14 @@ declare_clippy_lint! {
/// ```
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let s = &String::new();
///
/// let a: &String = &* s;
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # let s = &String::new();
/// let a: &String = s;
/// ```
@@ -49,69 +49,64 @@ declare_lint_pass!(BorrowDerefRef => [BORROW_DEREF_REF]);
impl<'tcx> LateLintPass<'tcx> for BorrowDerefRef {
fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &rustc_hir::Expr<'tcx>) {
- if_chain! {
- if !e.span.from_expansion();
- if let ExprKind::AddrOf(_, Mutability::Not, addrof_target) = e.kind;
- if !addrof_target.span.from_expansion();
- if let ExprKind::Unary(UnOp::Deref, deref_target) = addrof_target.kind;
- if !deref_target.span.from_expansion();
- if !matches!(deref_target.kind, ExprKind::Unary(UnOp::Deref, ..) );
- let ref_ty = cx.typeck_results().expr_ty(deref_target);
- if let ty::Ref(_, inner_ty, Mutability::Not) = ref_ty.kind();
- if !is_from_proc_macro(cx, e);
- then{
-
- if let Some(parent_expr) = get_parent_expr(cx, e){
- if matches!(parent_expr.kind, ExprKind::Unary(UnOp::Deref, ..)) &&
- !is_lint_allowed(cx, DEREF_ADDROF, parent_expr.hir_id) {
- return;
- }
+ if !e.span.from_expansion()
+ && let ExprKind::AddrOf(_, Mutability::Not, addrof_target) = e.kind
+ && !addrof_target.span.from_expansion()
+ && let ExprKind::Unary(UnOp::Deref, deref_target) = addrof_target.kind
+ && !deref_target.span.from_expansion()
+ && !matches!(deref_target.kind, ExprKind::Unary(UnOp::Deref, ..))
+ && let ref_ty = cx.typeck_results().expr_ty(deref_target)
+ && let ty::Ref(_, inner_ty, Mutability::Not) = ref_ty.kind()
+ {
+ if let Some(parent_expr) = get_parent_expr(cx, e) {
+ if matches!(parent_expr.kind, ExprKind::Unary(UnOp::Deref, ..))
+ && !is_lint_allowed(cx, DEREF_ADDROF, parent_expr.hir_id)
+ {
+ return;
+ }
- // modification to `&mut &*x` is different from `&mut x`
- if matches!(deref_target.kind, ExprKind::Path(..)
- | ExprKind::Field(..)
- | ExprKind::Index(..)
- | ExprKind::Unary(UnOp::Deref, ..))
- && matches!(parent_expr.kind, ExprKind::AddrOf(_, Mutability::Mut, _)) {
- return;
- }
+ // modification to `&mut &*x` is different from `&mut x`
+ if matches!(
+ deref_target.kind,
+ ExprKind::Path(..) | ExprKind::Field(..) | ExprKind::Index(..) | ExprKind::Unary(UnOp::Deref, ..)
+ ) && matches!(parent_expr.kind, ExprKind::AddrOf(_, Mutability::Mut, _))
+ {
+ return;
}
+ }
+ if is_from_proc_macro(cx, e) {
+ return;
+ }
- span_lint_and_then(
- cx,
- BORROW_DEREF_REF,
- e.span,
- "deref on an immutable reference",
- |diag| {
- diag.span_suggestion(
- e.span,
- "if you would like to reborrow, try removing `&*`",
- snippet_opt(cx, deref_target.span).unwrap(),
- Applicability::MachineApplicable
- );
+ span_lint_and_then(
+ cx,
+ BORROW_DEREF_REF,
+ e.span,
+ "deref on an immutable reference",
+ |diag| {
+ diag.span_suggestion(
+ e.span,
+ "if you would like to reborrow, try removing `&*`",
+ snippet_opt(cx, deref_target.span).unwrap(),
+ Applicability::MachineApplicable,
+ );
- // has deref trait -> give 2 help
- // doesn't have deref trait -> give 1 help
- if let Some(deref_trait_id) = cx.tcx.lang_items().deref_trait(){
- if !implements_trait(cx, *inner_ty, deref_trait_id, &[]) {
- return;
- }
+ // has deref trait -> give 2 help
+ // doesn't have deref trait -> give 1 help
+ if let Some(deref_trait_id) = cx.tcx.lang_items().deref_trait() {
+ if !implements_trait(cx, *inner_ty, deref_trait_id, &[]) {
+ return;
}
-
- diag.span_suggestion(
- e.span,
- "if you would like to deref, try using `&**`",
- format!(
- "&**{}",
- &snippet_opt(cx, deref_target.span).unwrap(),
- ),
- Applicability::MaybeIncorrect
- );
-
}
- );
- }
+ diag.span_suggestion(
+ e.span,
+ "if you would like to deref, try using `&**`",
+ format!("&**{}", &snippet_opt(cx, deref_target.span).unwrap()),
+ Applicability::MaybeIncorrect,
+ );
+ },
+ );
}
}
}
diff --git a/src/tools/clippy/clippy_lints/src/box_default.rs b/src/tools/clippy/clippy_lints/src/box_default.rs
index fa9c525fc..9c78c6e53 100644
--- a/src/tools/clippy/clippy_lints/src/box_default.rs
+++ b/src/tools/clippy/clippy_lints/src/box_default.rs
@@ -1,8 +1,9 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::macros::macro_backtrace;
use clippy_utils::ty::expr_sig;
-use clippy_utils::{get_parent_node, is_default_equivalent, match_path, path_def_id, paths};
+use clippy_utils::{get_parent_node, is_default_equivalent, path_def_id};
use rustc_errors::Applicability;
+use rustc_hir::def::Res;
use rustc_hir::intravisit::{walk_ty, Visitor};
use rustc_hir::{Block, Expr, ExprKind, Local, Node, QPath, TyKind};
use rustc_lint::{LateContext, LateLintPass, LintContext};
@@ -23,11 +24,11 @@ declare_clippy_lint! {
/// [in certain cases](https://nnethercote.github.io/perf-book/standard-library-types.html#box).
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let x: Box<String> = Box::new(Default::default());
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// let x: Box<String> = Box::default();
/// ```
#[clippy::version = "1.66.0"]
@@ -55,24 +56,26 @@ impl LateLintPass<'_> for BoxDefault {
expr.span,
"`Box::new(_)` of default value",
"try",
- if is_plain_default(arg_path) || given_type(cx, expr) {
+ if is_plain_default(cx, arg_path) || given_type(cx, expr) {
"Box::default()".into()
} else if let Some(arg_ty) = cx.typeck_results().expr_ty(arg).make_suggestable(cx.tcx, true) {
with_forced_trimmed_paths!(format!("Box::<{arg_ty}>::default()"))
} else {
- return
+ return;
},
- Applicability::MachineApplicable
+ Applicability::MachineApplicable,
);
}
}
}
-fn is_plain_default(arg_path: &Expr<'_>) -> bool {
+fn is_plain_default(cx: &LateContext<'_>, arg_path: &Expr<'_>) -> bool {
// we need to match the actual path so we don't match e.g. "u8::default"
- if let ExprKind::Path(QPath::Resolved(None, path)) = &arg_path.kind {
+ if let ExprKind::Path(QPath::Resolved(None, path)) = &arg_path.kind
+ && let Res::Def(_, def_id) = path.res
+ {
// avoid generic parameters
- match_path(path, &paths::DEFAULT_TRAIT_METHOD) && path.segments.iter().all(|seg| seg.args.is_none())
+ cx.tcx.is_diagnostic_item(sym::default_fn, def_id) && path.segments.iter().all(|seg| seg.args.is_none())
} else {
false
}
@@ -107,7 +110,8 @@ fn given_type(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
Node::Expr(Expr {
kind: ExprKind::Call(path, args),
..
- }) | Node::Block(Block {
+ })
+ | Node::Block(Block {
expr:
Some(Expr {
kind: ExprKind::Call(path, args),
@@ -116,10 +120,10 @@ fn given_type(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
..
}),
) => {
- if let Some(index) = args.iter().position(|arg| arg.hir_id == expr.hir_id) &&
- let Some(sig) = expr_sig(cx, path) &&
- let Some(input) = sig.input(index) &&
- !cx.typeck_results().expr_ty_adjusted(expr).boxed_ty().is_trait()
+ if let Some(index) = args.iter().position(|arg| arg.hir_id == expr.hir_id)
+ && let Some(sig) = expr_sig(cx, path)
+ && let Some(input) = sig.input(index)
+ && !cx.typeck_results().expr_ty_adjusted(expr).boxed_ty().is_trait()
{
input.no_bound_vars().is_some()
} else {
diff --git a/src/tools/clippy/clippy_lints/src/cargo/common_metadata.rs b/src/tools/clippy/clippy_lints/src/cargo/common_metadata.rs
index 805121bcc..99fe6c1e7 100644
--- a/src/tools/clippy/clippy_lints/src/cargo/common_metadata.rs
+++ b/src/tools/clippy/clippy_lints/src/cargo/common_metadata.rs
@@ -3,7 +3,7 @@
use cargo_metadata::Metadata;
use clippy_utils::diagnostics::span_lint;
use rustc_lint::LateContext;
-use rustc_span::source_map::DUMMY_SP;
+use rustc_span::DUMMY_SP;
use super::CARGO_COMMON_METADATA;
diff --git a/src/tools/clippy/clippy_lints/src/cargo/feature_name.rs b/src/tools/clippy/clippy_lints/src/cargo/feature_name.rs
index 37c169dbd..9e69919c7 100644
--- a/src/tools/clippy/clippy_lints/src/cargo/feature_name.rs
+++ b/src/tools/clippy/clippy_lints/src/cargo/feature_name.rs
@@ -1,7 +1,7 @@
use cargo_metadata::Metadata;
use clippy_utils::diagnostics::span_lint_and_help;
use rustc_lint::LateContext;
-use rustc_span::source_map::DUMMY_SP;
+use rustc_span::DUMMY_SP;
use super::{NEGATIVE_FEATURE_NAMES, REDUNDANT_FEATURE_NAMES};
diff --git a/src/tools/clippy/clippy_lints/src/cargo/multiple_crate_versions.rs b/src/tools/clippy/clippy_lints/src/cargo/multiple_crate_versions.rs
index f9b17d45e..f7a5b1857 100644
--- a/src/tools/clippy/clippy_lints/src/cargo/multiple_crate_versions.rs
+++ b/src/tools/clippy/clippy_lints/src/cargo/multiple_crate_versions.rs
@@ -6,7 +6,7 @@ use if_chain::if_chain;
use itertools::Itertools;
use rustc_hir::def_id::LOCAL_CRATE;
use rustc_lint::LateContext;
-use rustc_span::source_map::DUMMY_SP;
+use rustc_span::DUMMY_SP;
use super::MULTIPLE_CRATE_VERSIONS;
diff --git a/src/tools/clippy/clippy_lints/src/cargo/wildcard_dependencies.rs b/src/tools/clippy/clippy_lints/src/cargo/wildcard_dependencies.rs
index 7fa6acbf5..4dcc9cbe3 100644
--- a/src/tools/clippy/clippy_lints/src/cargo/wildcard_dependencies.rs
+++ b/src/tools/clippy/clippy_lints/src/cargo/wildcard_dependencies.rs
@@ -2,7 +2,7 @@ use cargo_metadata::Metadata;
use clippy_utils::diagnostics::span_lint;
use if_chain::if_chain;
use rustc_lint::LateContext;
-use rustc_span::source_map::DUMMY_SP;
+use rustc_span::DUMMY_SP;
use super::WILDCARD_DEPENDENCIES;
diff --git a/src/tools/clippy/clippy_lints/src/casts/as_ptr_cast_mut.rs b/src/tools/clippy/clippy_lints/src/casts/as_ptr_cast_mut.rs
index 1e56ed5f4..55294f5f3 100644
--- a/src/tools/clippy/clippy_lints/src/casts/as_ptr_cast_mut.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/as_ptr_cast_mut.rs
@@ -9,12 +9,19 @@ use rustc_middle::ty::{self, Ty, TypeAndMut};
use super::AS_PTR_CAST_MUT;
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, cast_to: Ty<'_>) {
- if let ty::RawPtr(ptrty @ TypeAndMut { mutbl: Mutability::Mut, .. }) = cast_to.kind()
- && let ty::RawPtr(TypeAndMut { mutbl: Mutability::Not, .. }) =
- cx.typeck_results().node_type(cast_expr.hir_id).kind()
+ if let ty::RawPtr(
+ ptrty @ TypeAndMut {
+ mutbl: Mutability::Mut, ..
+ },
+ ) = cast_to.kind()
+ && let ty::RawPtr(TypeAndMut {
+ mutbl: Mutability::Not, ..
+ }) = cx.typeck_results().node_type(cast_expr.hir_id).kind()
&& let ExprKind::MethodCall(method_name, receiver, [], _) = cast_expr.peel_blocks().kind
&& method_name.ident.name == rustc_span::sym::as_ptr
- && let Some(as_ptr_did) = cx.typeck_results().type_dependent_def_id(cast_expr.peel_blocks().hir_id)
+ && let Some(as_ptr_did) = cx
+ .typeck_results()
+ .type_dependent_def_id(cast_expr.peel_blocks().hir_id)
&& let as_ptr_sig = cx.tcx.fn_sig(as_ptr_did).instantiate_identity()
&& let Some(first_param_ty) = as_ptr_sig.skip_binder().inputs().iter().next()
&& let ty::Ref(_, _, Mutability::Not) = first_param_ty.kind()
@@ -30,7 +37,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>,
&format!("casting the result of `as_ptr` to *{ptrty}"),
"replace with",
format!("{recv}.as_mut_ptr()"),
- applicability
+ applicability,
);
}
}
diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_abs_to_unsigned.rs b/src/tools/clippy/clippy_lints/src/casts/cast_abs_to_unsigned.rs
index 442262983..c16633483 100644
--- a/src/tools/clippy/clippy_lints/src/casts/cast_abs_to_unsigned.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/cast_abs_to_unsigned.rs
@@ -1,5 +1,5 @@
+use clippy_config::msrvs::{self, Msrv};
use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::sugg::Sugg;
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind};
diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_lossless.rs b/src/tools/clippy/clippy_lints/src/casts/cast_lossless.rs
index c586b572b..fe2455f4b 100644
--- a/src/tools/clippy/clippy_lints/src/casts/cast_lossless.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/cast_lossless.rs
@@ -1,6 +1,6 @@
+use clippy_config::msrvs::{self, Msrv};
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::in_constant;
-use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::source::snippet_opt;
use clippy_utils::ty::is_isize_or_usize;
use rustc_errors::Applicability;
diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_ptr_alignment.rs b/src/tools/clippy/clippy_lints/src/casts/cast_ptr_alignment.rs
index 1de691221..f12f03fbe 100644
--- a/src/tools/clippy/clippy_lints/src/casts/cast_ptr_alignment.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/cast_ptr_alignment.rs
@@ -1,6 +1,6 @@
use clippy_utils::diagnostics::span_lint;
use clippy_utils::ty::is_c_void;
-use clippy_utils::{get_parent_expr, is_hir_ty_cfg_dependant, match_any_def_paths, paths};
+use clippy_utils::{get_parent_expr, is_hir_ty_cfg_dependant};
use rustc_hir::{Expr, ExprKind, GenericArg};
use rustc_lint::LateContext;
use rustc_middle::ty::layout::LayoutOf;
@@ -26,8 +26,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) {
// There probably is no obvious reason to do this, just to be consistent with `as` cases.
&& !is_hir_ty_cfg_dependant(cx, cast_to)
{
- let (cast_from, cast_to) =
- (cx.typeck_results().expr_ty(self_arg), cx.typeck_results().expr_ty(expr));
+ let (cast_from, cast_to) = (cx.typeck_results().expr_ty(self_arg), cx.typeck_results().expr_ty(expr));
lint_cast_ptr_alignment(cx, expr, cast_from, cast_to);
}
}
@@ -75,16 +74,17 @@ fn is_used_as_unaligned(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
}
},
ExprKind::Call(func, [arg, ..]) if arg.hir_id == e.hir_id => {
- static PATHS: &[&[&str]] = &[
- paths::PTR_READ_UNALIGNED.as_slice(),
- paths::PTR_UNALIGNED_VOLATILE_LOAD.as_slice(),
- paths::PTR_UNALIGNED_VOLATILE_STORE.as_slice(),
- ];
-
if let ExprKind::Path(path) = &func.kind
&& let Some(def_id) = cx.qpath_res(path, func.hir_id).opt_def_id()
- && (match_any_def_paths(cx, def_id, PATHS).is_some()
- || cx.tcx.is_diagnostic_item(sym::ptr_write_unaligned, def_id))
+ && matches!(
+ cx.tcx.get_diagnostic_name(def_id),
+ Some(
+ sym::ptr_write_unaligned
+ | sym::ptr_read_unaligned
+ | sym::intrinsics_unaligned_volatile_load
+ | sym::intrinsics_unaligned_volatile_store
+ )
+ )
{
true
} else {
diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_slice_different_sizes.rs b/src/tools/clippy/clippy_lints/src/casts/cast_slice_different_sizes.rs
index 4d9cc4cac..d14104029 100644
--- a/src/tools/clippy/clippy_lints/src/casts/cast_slice_different_sizes.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/cast_slice_different_sizes.rs
@@ -1,5 +1,5 @@
+use clippy_config::msrvs::{self, Msrv};
use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::source;
use if_chain::if_chain;
use rustc_ast::Mutability;
diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_slice_from_raw_parts.rs b/src/tools/clippy/clippy_lints/src/casts/cast_slice_from_raw_parts.rs
index 5e0123842..badadf2c9 100644
--- a/src/tools/clippy/clippy_lints/src/casts/cast_slice_from_raw_parts.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/cast_slice_from_raw_parts.rs
@@ -1,13 +1,13 @@
+use clippy_config::msrvs::{self, Msrv};
use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::source::snippet_with_context;
-use clippy_utils::{match_def_path, paths};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::def_id::DefId;
use rustc_hir::{Expr, ExprKind};
use rustc_lint::LateContext;
use rustc_middle::ty::{self, Ty};
+use rustc_span::sym;
use super::CAST_SLICE_FROM_RAW_PARTS;
@@ -17,12 +17,10 @@ enum RawPartsKind {
}
fn raw_parts_kind(cx: &LateContext<'_>, did: DefId) -> Option<RawPartsKind> {
- if match_def_path(cx, did, &paths::SLICE_FROM_RAW_PARTS) {
- Some(RawPartsKind::Immutable)
- } else if match_def_path(cx, did, &paths::SLICE_FROM_RAW_PARTS_MUT) {
- Some(RawPartsKind::Mutable)
- } else {
- None
+ match cx.tcx.get_diagnostic_name(did)? {
+ sym::slice_from_raw_parts => Some(RawPartsKind::Immutable),
+ sym::slice_from_raw_parts_mut => Some(RawPartsKind::Mutable),
+ _ => None,
}
}
diff --git a/src/tools/clippy/clippy_lints/src/casts/mod.rs b/src/tools/clippy/clippy_lints/src/casts/mod.rs
index b00130ffd..49a90a2f3 100644
--- a/src/tools/clippy/clippy_lints/src/casts/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/mod.rs
@@ -22,8 +22,8 @@ mod unnecessary_cast;
mod utils;
mod zero_ptr;
+use clippy_config::msrvs::{self, Msrv};
use clippy_utils::is_hir_ty_cfg_dependant;
-use clippy_utils::msrvs::{self, Msrv};
use rustc_hir::{Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
@@ -45,7 +45,7 @@ declare_clippy_lint! {
/// those places in the code.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let x = u64::MAX;
/// x as f64;
/// ```
@@ -67,7 +67,7 @@ declare_clippy_lint! {
/// as a one-time check to see where numerical wrapping can arise.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let y: i8 = -1;
/// y as u128; // will return 18446744073709551615
/// ```
@@ -90,13 +90,13 @@ declare_clippy_lint! {
/// checks could be beneficial.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// fn as_u8(x: u64) -> u8 {
/// x as u8
/// }
/// ```
/// Use instead:
- /// ```
+ /// ```no_run
/// fn as_u8(x: u64) -> u8 {
/// if let Ok(x) = u8::try_from(x) {
/// x
@@ -132,7 +132,7 @@ declare_clippy_lint! {
/// example below.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// u32::MAX as i32; // will yield a value of `-1`
/// ```
#[clippy::version = "pre 1.29.0"]
@@ -155,7 +155,7 @@ declare_clippy_lint! {
/// people reading the code to know that the conversion is lossless.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// fn as_u64(x: u8) -> u64 {
/// x as u64
/// }
@@ -163,7 +163,7 @@ declare_clippy_lint! {
///
/// Using `::from` would look like this:
///
- /// ```rust
+ /// ```no_run
/// fn as_u64(x: u8) -> u64 {
/// u64::from(x)
/// }
@@ -191,14 +191,14 @@ declare_clippy_lint! {
/// intermediate references, raw pointers and trait objects may or may not work.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let _ = 2i32 as i32;
/// let _ = 0.5 as f32;
/// ```
///
/// Better:
///
- /// ```rust
+ /// ```no_run
/// let _ = 2_i32;
/// let _ = 0.5_f32;
/// ```
@@ -223,7 +223,7 @@ declare_clippy_lint! {
/// u64-> u8 -> u16 can be fine. Miri is able to do a more in-depth analysis.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let _ = (&1u8 as *const u8) as *const u16;
/// let _ = (&mut 1u8 as *mut u8) as *mut u16;
///
@@ -249,13 +249,13 @@ declare_clippy_lint! {
/// Casting to isize also doesn't make sense since there are no signed addresses.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// fn fun() -> i32 { 1 }
/// let _ = fun as i64;
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # fn fun() -> i32 { 1 }
/// let _ = fun as usize;
/// ```
@@ -276,7 +276,7 @@ declare_clippy_lint! {
/// a comment) to perform the truncation.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// fn fn1() -> i16 {
/// 1
/// };
@@ -284,7 +284,7 @@ declare_clippy_lint! {
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// // Cast to usize first, then comment with the reason for the truncation
/// fn fn1() -> i16 {
/// 1
@@ -310,7 +310,7 @@ declare_clippy_lint! {
/// pointer casts in your code.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// // fn1 is cast as `usize`
/// fn fn1() -> u16 {
/// 1
@@ -319,7 +319,7 @@ declare_clippy_lint! {
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// // maybe you intended to call the function?
/// fn fn2() -> u16 {
/// 1
@@ -378,14 +378,14 @@ declare_clippy_lint! {
/// it cannot accidentally change the pointer's mutability nor cast the pointer to other types like `usize`.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let ptr: *const u32 = &42_u32;
/// let mut_ptr: *mut u32 = &mut 42_u32;
/// let _ = ptr as *const i32;
/// let _ = mut_ptr as *mut i32;
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// let ptr: *const u32 = &42_u32;
/// let mut_ptr: *mut u32 = &mut 42_u32;
/// let _ = ptr.cast::<i32>();
@@ -408,13 +408,13 @@ declare_clippy_lint! {
/// type.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let ptr: *const u32 = &42_u32;
/// let mut_ptr = ptr as *mut u32;
/// let ptr = mut_ptr as *const u32;
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// let ptr: *const u32 = &42_u32;
/// let mut_ptr = ptr.cast_mut();
/// let ptr = mut_ptr.cast_const();
@@ -434,7 +434,7 @@ declare_clippy_lint! {
/// The resulting integral value will not match the value of the variant it came from.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// enum E { X = 256 };
/// let _ = E::X as u8;
/// ```
@@ -457,7 +457,7 @@ declare_clippy_lint! {
///
/// ### Example
/// // Missing data
- /// ```rust
+ /// ```no_run
/// let a = [1_i32, 2, 3, 4];
/// let p = &a as *const [i32] as *const [u8];
/// unsafe {
@@ -465,7 +465,7 @@ declare_clippy_lint! {
/// }
/// ```
/// // Undefined Behavior (note: also potential alignment issues)
- /// ```rust
+ /// ```no_run
/// let a = [1_u8, 2, 3, 4];
/// let p = &a as *const [u8] as *const [u32];
/// unsafe {
@@ -473,7 +473,7 @@ declare_clippy_lint! {
/// }
/// ```
/// Instead use `ptr::slice_from_raw_parts` to construct a slice from a data pointer and the correct length
- /// ```rust
+ /// ```no_run
/// let a = [1_i32, 2, 3, 4];
/// let old_ptr = &a as *const [i32];
/// // The data pointer is cast to a pointer to the target `u8` not `[u8]`
@@ -497,7 +497,7 @@ declare_clippy_lint! {
/// The cast is easily confused with casting a c-like enum value to an integer.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// enum E { X(i32) };
/// let _ = E::X as usize;
/// ```
@@ -515,12 +515,12 @@ declare_clippy_lint! {
/// The `unsigned_abs()` method avoids panic when called on the MIN value.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let x: i32 = -42;
/// let y: u32 = x.abs() as u32;
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// let x: i32 = -42;
/// let y: u32 = x.unsigned_abs();
/// ```
@@ -541,13 +541,13 @@ declare_clippy_lint! {
/// The lint is allowed by default as using `_` is less wordy than always specifying the type.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// fn foo(n: usize) {}
/// let n: u16 = 256;
/// foo(n as _);
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// fn foo(n: usize) {}
/// let n: u16 = 256;
/// foo(n as usize);
@@ -570,7 +570,7 @@ declare_clippy_lint! {
/// Read the `ptr::addr_of` docs for more information.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let val = 1;
/// let p = &val as *const i32;
///
@@ -578,7 +578,7 @@ declare_clippy_lint! {
/// let p_mut = &mut val_mut as *mut i32;
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// let val = 1;
/// let p = std::ptr::addr_of!(val);
///
@@ -627,13 +627,13 @@ declare_clippy_lint! {
/// mutability is used, making it unlikely that having it as a mutable pointer is correct.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let mut vec = Vec::<u8>::with_capacity(1);
/// let ptr = vec.as_ptr() as *mut u8;
/// unsafe { ptr.write(4) }; // UNDEFINED BEHAVIOUR
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// let mut vec = Vec::<u8>::with_capacity(1);
/// let ptr = vec.as_mut_ptr();
/// unsafe { ptr.write(4) };
@@ -675,12 +675,12 @@ declare_clippy_lint! {
/// {`std`, `core`}`::ptr::`{`null`, `null_mut`}.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let a = 0 as *const u32;
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// let a = std::ptr::null::<u32>();
/// ```
#[clippy::version = "pre 1.29.0"]
diff --git a/src/tools/clippy/clippy_lints/src/casts/ptr_as_ptr.rs b/src/tools/clippy/clippy_lints/src/casts/ptr_as_ptr.rs
index 181dbcf6e..0c555c1ac 100644
--- a/src/tools/clippy/clippy_lints/src/casts/ptr_as_ptr.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/ptr_as_ptr.rs
@@ -1,5 +1,5 @@
+use clippy_config::msrvs::{self, Msrv};
use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::sugg::Sugg;
use rustc_errors::Applicability;
diff --git a/src/tools/clippy/clippy_lints/src/casts/ptr_cast_constness.rs b/src/tools/clippy/clippy_lints/src/casts/ptr_cast_constness.rs
index ce1ab1091..0172e9336 100644
--- a/src/tools/clippy/clippy_lints/src/casts/ptr_cast_constness.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/ptr_cast_constness.rs
@@ -1,5 +1,5 @@
+use clippy_config::msrvs::{self, Msrv};
use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::msrvs::{Msrv, POINTER_CAST_CONSTNESS};
use clippy_utils::sugg::Sugg;
use if_chain::if_chain;
use rustc_errors::Applicability;
@@ -18,7 +18,7 @@ pub(super) fn check<'tcx>(
msrv: &Msrv,
) {
if_chain! {
- if msrv.meets(POINTER_CAST_CONSTNESS);
+ if msrv.meets(msrvs::POINTER_CAST_CONSTNESS);
if let ty::RawPtr(TypeAndMut { mutbl: from_mutbl, ty: from_ty }) = cast_from.kind();
if let ty::RawPtr(TypeAndMut { mutbl: to_mutbl, ty: to_ty }) = cast_to.kind();
if matches!((from_mutbl, to_mutbl),
diff --git a/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs b/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs
index 86057bb74..61bfce07e 100644
--- a/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs
@@ -97,7 +97,9 @@ pub(super) fn check<'tcx>(
}
// skip cast of fn call that returns type alias
- if let ExprKind::Cast(inner, ..) = expr.kind && is_cast_from_ty_alias(cx, inner, cast_from) {
+ if let ExprKind::Cast(inner, ..) = expr.kind
+ && is_cast_from_ty_alias(cx, inner, cast_from)
+ {
return false;
}
@@ -189,11 +191,10 @@ fn lint_unnecessary_cast(
let sugg = if let Some(parent_expr) = get_parent_expr(cx, expr)
&& let ExprKind::MethodCall(..) = parent_expr.kind
&& literal_str.starts_with('-')
- {
- format!("({literal_str}_{cast_to})")
-
- } else {
- format!("{literal_str}_{cast_to}")
+ {
+ format!("({literal_str}_{cast_to})")
+ } else {
+ format!("{literal_str}_{cast_to}")
};
span_lint_and_sugg(
@@ -269,7 +270,9 @@ fn is_cast_from_ty_alias<'tcx>(cx: &LateContext<'tcx>, expr: impl Visitable<'tcx
&& let Some(parent) = get_parent_node(cx.tcx, hir_id)
&& let Node::Local(l) = parent
{
- if let Some(e) = l.init && is_cast_from_ty_alias(cx, e, cast_from) {
+ if let Some(e) = l.init
+ && is_cast_from_ty_alias(cx, e, cast_from)
+ {
return ControlFlow::Break::<()>(());
}
diff --git a/src/tools/clippy/clippy_lints/src/checked_conversions.rs b/src/tools/clippy/clippy_lints/src/checked_conversions.rs
index 9102a89e3..d31c2268a 100644
--- a/src/tools/clippy/clippy_lints/src/checked_conversions.rs
+++ b/src/tools/clippy/clippy_lints/src/checked_conversions.rs
@@ -1,7 +1,7 @@
//! lint on manually implemented checked conversions that could be transformed into `try_from`
+use clippy_config::msrvs::{self, Msrv};
use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::{in_constant, is_integer_literal, SpanlessEq};
use if_chain::if_chain;
@@ -19,13 +19,13 @@ declare_clippy_lint! {
/// Reduces the readability of statements & is error prone.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # let foo: u32 = 5;
/// foo <= i32::MAX as u32;
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # let foo = 1;
/// # #[allow(unused)]
/// i32::try_from(foo).is_ok();
diff --git a/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs b/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs
index a8926b29a..74ecaa60c 100644
--- a/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs
+++ b/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs
@@ -12,8 +12,7 @@ use rustc_hir::{Body, Expr, ExprKind, FnDecl};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::def_id::LocalDefId;
-use rustc_span::source_map::Span;
-use rustc_span::{sym, BytePos};
+use rustc_span::{sym, BytePos, Span};
declare_clippy_lint! {
/// ### What it does
diff --git a/src/tools/clippy/clippy_lints/src/collapsible_if.rs b/src/tools/clippy/clippy_lints/src/collapsible_if.rs
index b38e09dc0..d21ef195d 100644
--- a/src/tools/clippy/clippy_lints/src/collapsible_if.rs
+++ b/src/tools/clippy/clippy_lints/src/collapsible_if.rs
@@ -32,7 +32,7 @@ declare_clippy_lint! {
/// makes code look more complex than it really is.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # let (x, y) = (true, true);
/// if x {
/// if y {
@@ -42,7 +42,7 @@ declare_clippy_lint! {
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # let (x, y) = (true, true);
/// if x && y {
/// // …
diff --git a/src/tools/clippy/clippy_lints/src/collection_is_never_read.rs b/src/tools/clippy/clippy_lints/src/collection_is_never_read.rs
index ac5ac542c..1dfc2e251 100644
--- a/src/tools/clippy/clippy_lints/src/collection_is_never_read.rs
+++ b/src/tools/clippy/clippy_lints/src/collection_is_never_read.rs
@@ -20,7 +20,7 @@ declare_clippy_lint! {
/// instead.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # let samples = vec![3, 1, 2];
/// let mut sorted_samples = samples.clone();
/// sorted_samples.sort();
@@ -29,7 +29,7 @@ declare_clippy_lint! {
/// }
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # let samples = vec![3, 1, 2];
/// let mut sorted_samples = samples.clone();
/// sorted_samples.sort();
diff --git a/src/tools/clippy/clippy_lints/src/crate_in_macro_def.rs b/src/tools/clippy/clippy_lints/src/crate_in_macro_def.rs
index 726674d88..a2005638d 100644
--- a/src/tools/clippy/clippy_lints/src/crate_in_macro_def.rs
+++ b/src/tools/clippy/clippy_lints/src/crate_in_macro_def.rs
@@ -18,7 +18,7 @@ declare_clippy_lint! {
/// https://doc.rust-lang.org/reference/macros-by-example.html#hygiene
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// #[macro_export]
/// macro_rules! print_message {
/// () => {
@@ -28,7 +28,7 @@ declare_clippy_lint! {
/// pub const MESSAGE: &str = "Hello!";
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// #[macro_export]
/// macro_rules! print_message {
/// () => {
diff --git a/src/tools/clippy/clippy_lints/src/create_dir.rs b/src/tools/clippy/clippy_lints/src/create_dir.rs
index 878248a6b..2bca695c4 100644
--- a/src/tools/clippy/clippy_lints/src/create_dir.rs
+++ b/src/tools/clippy/clippy_lints/src/create_dir.rs
@@ -1,11 +1,11 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet;
-use clippy_utils::{match_def_path, paths};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::sym;
declare_clippy_lint! {
/// ### What it does
@@ -37,7 +37,7 @@ impl LateLintPass<'_> for CreateDir {
if let ExprKind::Call(func, [arg, ..]) = expr.kind;
if let ExprKind::Path(ref path) = func.kind;
if let Some(def_id) = cx.qpath_res(path, func.hir_id).opt_def_id();
- if match_def_path(cx, def_id, &paths::STD_FS_CREATE_DIR);
+ if cx.tcx.is_diagnostic_item(sym::fs_create_dir, def_id);
then {
span_lint_and_sugg(
cx,
diff --git a/src/tools/clippy/clippy_lints/src/declared_lints.rs b/src/tools/clippy/clippy_lints/src/declared_lints.rs
index 4d1281ec1..1a646ba38 100644
--- a/src/tools/clippy/clippy_lints/src/declared_lints.rs
+++ b/src/tools/clippy/clippy_lints/src/declared_lints.rs
@@ -6,8 +6,6 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
#[cfg(feature = "internal")]
crate::utils::internal_lints::almost_standard_lint_formulation::ALMOST_STANDARD_LINT_FORMULATION_INFO,
#[cfg(feature = "internal")]
- crate::utils::internal_lints::clippy_lints_internal::CLIPPY_LINTS_INTERNAL_INFO,
- #[cfg(feature = "internal")]
crate::utils::internal_lints::collapsible_calls::COLLAPSIBLE_SPAN_LINT_CALLS_INFO,
#[cfg(feature = "internal")]
crate::utils::internal_lints::compiler_lint_functions::COMPILER_LINT_FUNCTIONS_INFO,
@@ -30,6 +28,8 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
#[cfg(feature = "internal")]
crate::utils::internal_lints::lint_without_lint_pass::MISSING_CLIPPY_VERSION_ATTRIBUTE_INFO,
#[cfg(feature = "internal")]
+ crate::utils::internal_lints::metadata_collector::METADATA_COLLECTOR_INFO,
+ #[cfg(feature = "internal")]
crate::utils::internal_lints::msrv_attr_impl::MISSING_MSRV_ATTR_IMPL_INFO,
#[cfg(feature = "internal")]
crate::utils::internal_lints::outer_expn_data_pass::OUTER_EXPN_EXPN_DATA_INFO,
@@ -37,6 +37,8 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::utils::internal_lints::produce_ice::PRODUCE_ICE_INFO,
#[cfg(feature = "internal")]
crate::utils::internal_lints::unnecessary_def_path::UNNECESSARY_DEF_PATH_INFO,
+ #[cfg(feature = "internal")]
+ crate::utils::internal_lints::unsorted_clippy_utils_paths::UNSORTED_CLIPPY_UTILS_PATHS_INFO,
crate::absolute_paths::ABSOLUTE_PATHS_INFO,
crate::allow_attributes::ALLOW_ATTRIBUTES_INFO,
crate::almost_complete_range::ALMOST_COMPLETE_RANGE_INFO,
@@ -154,9 +156,6 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::endian_bytes::LITTLE_ENDIAN_BYTES_INFO,
crate::entry::MAP_ENTRY_INFO,
crate::enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT_INFO,
- crate::enum_variants::ENUM_VARIANT_NAMES_INFO,
- crate::enum_variants::MODULE_INCEPTION_INFO,
- crate::enum_variants::MODULE_NAME_REPETITIONS_INFO,
crate::equatable_if_let::EQUATABLE_IF_LET_INFO,
crate::error_impl_error::ERROR_IMPL_ERROR_INFO,
crate::escape::BOXED_LOCAL_INFO,
@@ -226,9 +225,15 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::instant_subtraction::UNCHECKED_DURATION_SUBTRACTION_INFO,
crate::int_plus_one::INT_PLUS_ONE_INFO,
crate::invalid_upcast_comparisons::INVALID_UPCAST_COMPARISONS_INFO,
+ crate::item_name_repetitions::ENUM_VARIANT_NAMES_INFO,
+ crate::item_name_repetitions::MODULE_INCEPTION_INFO,
+ crate::item_name_repetitions::MODULE_NAME_REPETITIONS_INFO,
+ crate::item_name_repetitions::STRUCT_FIELD_NAMES_INFO,
crate::items_after_statements::ITEMS_AFTER_STATEMENTS_INFO,
crate::items_after_test_module::ITEMS_AFTER_TEST_MODULE_INFO,
crate::iter_not_returning_iterator::ITER_NOT_RETURNING_ITERATOR_INFO,
+ crate::iter_without_into_iter::INTO_ITER_WITHOUT_ITER_INFO,
+ crate::iter_without_into_iter::ITER_WITHOUT_INTO_ITER_INFO,
crate::large_const_arrays::LARGE_CONST_ARRAYS_INFO,
crate::large_enum_variant::LARGE_ENUM_VARIANT_INFO,
crate::large_futures::LARGE_FUTURES_INFO,
@@ -269,6 +274,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::loops::NEVER_LOOP_INFO,
crate::loops::SAME_ITEM_PUSH_INFO,
crate::loops::SINGLE_ELEMENT_LOOP_INFO,
+ crate::loops::UNUSED_ENUMERATE_INDEX_INFO,
crate::loops::WHILE_IMMUTABLE_CONDITION_INFO,
crate::loops::WHILE_LET_LOOP_INFO,
crate::loops::WHILE_LET_ON_ITERATOR_INFO,
@@ -280,6 +286,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::manual_clamp::MANUAL_CLAMP_INFO,
crate::manual_float_methods::MANUAL_IS_FINITE_INFO,
crate::manual_float_methods::MANUAL_IS_INFINITE_INFO,
+ crate::manual_hash_one::MANUAL_HASH_ONE_INFO,
crate::manual_is_ascii_check::MANUAL_IS_ASCII_CHECK_INFO,
crate::manual_let_else::MANUAL_LET_ELSE_INFO,
crate::manual_main_separator_str::MANUAL_MAIN_SEPARATOR_STR_INFO,
@@ -424,6 +431,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::methods::TYPE_ID_ON_BOX_INFO,
crate::methods::UNINIT_ASSUMED_INIT_INFO,
crate::methods::UNIT_HASH_INFO,
+ crate::methods::UNNECESSARY_FALLIBLE_CONVERSIONS_INFO,
crate::methods::UNNECESSARY_FILTER_MAP_INFO,
crate::methods::UNNECESSARY_FIND_MAP_INFO,
crate::methods::UNNECESSARY_FOLD_INFO,
@@ -437,6 +445,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::methods::USELESS_ASREF_INFO,
crate::methods::VEC_RESIZE_TO_ZERO_INFO,
crate::methods::VERBOSE_FILE_READS_INFO,
+ crate::methods::WAKER_CLONE_WAKE_INFO,
crate::methods::WRONG_SELF_CONVENTION_INFO,
crate::methods::ZST_OFFSET_INFO,
crate::min_ident_chars::MIN_IDENT_CHARS_INFO,
diff --git a/src/tools/clippy/clippy_lints/src/default.rs b/src/tools/clippy/clippy_lints/src/default.rs
index 763ad0264..c74b2b883 100644
--- a/src/tools/clippy/clippy_lints/src/default.rs
+++ b/src/tools/clippy/clippy_lints/src/default.rs
@@ -1,9 +1,7 @@
use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_sugg};
use clippy_utils::source::snippet_with_context;
use clippy_utils::ty::{has_drop, is_copy};
-use clippy_utils::{
- any_parent_is_automatically_derived, contains_name, get_parent_expr, is_from_proc_macro, match_def_path, paths,
-};
+use clippy_utils::{any_parent_is_automatically_derived, contains_name, get_parent_expr, is_from_proc_macro};
use if_chain::if_chain;
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::Applicability;
@@ -14,7 +12,7 @@ use rustc_middle::ty;
use rustc_middle::ty::print::with_forced_trimmed_paths;
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::symbol::{Ident, Symbol};
-use rustc_span::Span;
+use rustc_span::{sym, Span};
declare_clippy_lint! {
/// ### What it does
@@ -25,12 +23,12 @@ declare_clippy_lint! {
/// generic `Default`.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let s: String = Default::default();
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// let s = String::default();
/// ```
#[clippy::version = "pre 1.29.0"]
@@ -51,7 +49,7 @@ declare_clippy_lint! {
/// Assignments to patterns that are of tuple type are not linted.
///
/// ### Example
- /// ```
+ /// ```no_run
/// # #[derive(Default)]
/// # struct A { i: i32 }
/// let mut a: A = Default::default();
@@ -59,7 +57,7 @@ declare_clippy_lint! {
/// ```
///
/// Use instead:
- /// ```
+ /// ```no_run
/// # #[derive(Default)]
/// # struct A { i: i32 }
/// let a = A {
@@ -91,7 +89,7 @@ impl<'tcx> LateLintPass<'tcx> for Default {
if !any_parent_is_automatically_derived(cx.tcx, expr.hir_id);
if let ExprKind::Path(ref qpath) = path.kind;
if let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id();
- if match_def_path(cx, def_id, &paths::DEFAULT_TRAIT_METHOD);
+ if cx.tcx.is_diagnostic_item(sym::default_fn, def_id);
if !is_update_syntax_base(cx, expr);
// Detect and ignore <Foo as Default>::default() because these calls do explicitly name the type.
if let QPath::Resolved(None, _path) = qpath;
@@ -268,7 +266,7 @@ fn is_expr_default<'tcx>(expr: &'tcx Expr<'tcx>, cx: &LateContext<'tcx>) -> bool
if let Res::Def(_, def_id) = cx.qpath_res(qpath, fn_expr.hir_id);
then {
// right hand side of assignment is `Default::default`
- match_def_path(cx, def_id, &paths::DEFAULT_TRAIT_METHOD)
+ cx.tcx.is_diagnostic_item(sym::default_fn, def_id)
} else {
false
}
diff --git a/src/tools/clippy/clippy_lints/src/default_constructed_unit_structs.rs b/src/tools/clippy/clippy_lints/src/default_constructed_unit_structs.rs
index a294c6937..bf070432e 100644
--- a/src/tools/clippy/clippy_lints/src/default_constructed_unit_structs.rs
+++ b/src/tools/clippy/clippy_lints/src/default_constructed_unit_structs.rs
@@ -1,5 +1,5 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::{is_ty_alias, match_def_path, paths};
+use clippy_utils::is_ty_alias;
use hir::def::Res;
use hir::ExprKind;
use rustc_errors::Applicability;
@@ -7,6 +7,7 @@ use rustc_hir as hir;
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty;
use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::sym;
declare_clippy_lint! {
/// ### What it does
@@ -16,7 +17,7 @@ declare_clippy_lint! {
/// This adds code complexity and an unnecessary function call.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # use std::marker::PhantomData;
/// #[derive(Default)]
/// struct S<T> {
@@ -28,7 +29,7 @@ declare_clippy_lint! {
/// };
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # use std::marker::PhantomData;
/// struct S<T> {
/// _marker: PhantomData<T>
@@ -63,7 +64,7 @@ impl LateLintPass<'_> for DefaultConstructedUnitStructs {
// `<Foo as Bar>::Assoc` cannot be used as a constructor
if !is_alias(*base);
if let Res::Def(_, def_id) = cx.qpath_res(qpath, fn_expr.hir_id);
- if match_def_path(cx, def_id, &paths::DEFAULT_TRAIT_METHOD);
+ if cx.tcx.is_diagnostic_item(sym::default_fn, def_id);
// make sure we have a struct with no fields (unit struct)
if let ty::Adt(def, ..) = cx.typeck_results().expr_ty(expr).kind();
if def.is_struct();
diff --git a/src/tools/clippy/clippy_lints/src/default_instead_of_iter_empty.rs b/src/tools/clippy/clippy_lints/src/default_instead_of_iter_empty.rs
index 572990aab..553b670fd 100644
--- a/src/tools/clippy/clippy_lints/src/default_instead_of_iter_empty.rs
+++ b/src/tools/clippy/clippy_lints/src/default_instead_of_iter_empty.rs
@@ -1,11 +1,11 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::last_path_segment;
use clippy_utils::source::snippet_with_context;
-use clippy_utils::{last_path_segment, match_def_path, paths};
use rustc_errors::Applicability;
use rustc_hir::{def, Expr, ExprKind, GenericArg, QPath, TyKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::SyntaxContext;
+use rustc_span::{sym, SyntaxContext};
declare_clippy_lint! {
/// ### What it does
@@ -14,12 +14,12 @@ declare_clippy_lint! {
/// ### Why is this bad?
/// `std::iter::empty()` is the more idiomatic way.
/// ### Example
- /// ```rust
+ /// ```no_run
/// let _ = std::iter::Empty::<usize>::default();
/// let iter: std::iter::Empty<usize> = std::iter::Empty::default();
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// let _ = std::iter::empty::<usize>();
/// let iter: std::iter::Empty<usize> = std::iter::empty();
/// ```
@@ -37,7 +37,7 @@ impl<'tcx> LateLintPass<'tcx> for DefaultIterEmpty {
&& let TyKind::Path(ty_path) = &ty.kind
&& let QPath::Resolved(None, path) = ty_path
&& let def::Res::Def(_, def_id) = &path.res
- && match_def_path(cx, *def_id, &paths::ITER_EMPTY)
+ && cx.tcx.is_diagnostic_item(sym::IterEmpty, *def_id)
&& let ctxt = expr.span.ctxt()
&& ty.span.ctxt() == ctxt
{
@@ -68,7 +68,10 @@ fn make_sugg(
_ => None,
})
{
- format!("std::iter::empty::<{}>()", snippet_with_context(cx, iter_ty.span, ctxt, "..", applicability).0)
+ format!(
+ "std::iter::empty::<{}>()",
+ snippet_with_context(cx, iter_ty.span, ctxt, "..", applicability).0
+ )
} else {
"std::iter::empty()".to_owned()
}
diff --git a/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs b/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs
index d09428dbc..b296ea20f 100644
--- a/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs
+++ b/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs
@@ -31,13 +31,13 @@ declare_clippy_lint! {
/// This lint can only be allowed at the function level or above.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let i = 10;
/// let f = 1.23;
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// let i = 10i32;
/// let f = 1.23f64;
/// ```
diff --git a/src/tools/clippy/clippy_lints/src/default_union_representation.rs b/src/tools/clippy/clippy_lints/src/default_union_representation.rs
index 63ec81950..8c6749a95 100644
--- a/src/tools/clippy/clippy_lints/src/default_union_representation.rs
+++ b/src/tools/clippy/clippy_lints/src/default_union_representation.rs
@@ -17,7 +17,7 @@ declare_clippy_lint! {
/// specified layout. These cases may lead to undefined behavior in unsafe blocks.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// union Foo {
/// a: i32,
/// b: u32,
@@ -30,7 +30,7 @@ declare_clippy_lint! {
/// }
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// #[repr(C)]
/// union Foo {
/// a: i32,
diff --git a/src/tools/clippy/clippy_lints/src/dereference.rs b/src/tools/clippy/clippy_lints/src/dereference.rs
index 148773856..6c109a51f 100644
--- a/src/tools/clippy/clippy_lints/src/dereference.rs
+++ b/src/tools/clippy/clippy_lints/src/dereference.rs
@@ -29,14 +29,14 @@ declare_clippy_lint! {
/// when not part of a method chain.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// use std::ops::Deref;
/// let a: &mut String = &mut String::from("foo");
/// let b: &str = a.deref();
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// let a: &mut String = &mut String::from("foo");
/// let b = &*a;
/// ```
@@ -68,7 +68,7 @@ declare_clippy_lint! {
/// in such a case can change the semantics of the code.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// fn fun(_a: &i32) {}
///
/// let x: &i32 = &&&&&&5;
@@ -76,7 +76,7 @@ declare_clippy_lint! {
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # fn fun(_a: &i32) {}
/// let x: &i32 = &5;
/// fun(x);
@@ -95,7 +95,7 @@ declare_clippy_lint! {
/// The address-of operator at the use site is clearer about the need for a reference.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let x = Some("");
/// if let Some(ref x) = x {
/// // use `x` here
@@ -103,7 +103,7 @@ declare_clippy_lint! {
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// let x = Some("");
/// if let Some(x) = x {
/// // use `&x` here
@@ -123,12 +123,12 @@ declare_clippy_lint! {
/// This unnecessarily complicates the code.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let x = String::new();
/// let y: &str = &*x;
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// let x = String::new();
/// let y: &str = &x;
/// ```
@@ -353,23 +353,26 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> {
// priority.
if let Some(fn_id) = typeck.type_dependent_def_id(hir_id)
&& let Some(trait_id) = cx.tcx.trait_of_item(fn_id)
- && let arg_ty
- = cx.tcx.erase_regions(use_cx.adjustments.last().map_or(expr_ty, |a| a.target))
+ && let arg_ty = cx
+ .tcx
+ .erase_regions(use_cx.adjustments.last().map_or(expr_ty, |a| a.target))
&& let ty::Ref(_, sub_ty, _) = *arg_ty.kind()
&& let args = cx
.typeck_results()
- .node_args_opt(hir_id).map(|args| &args[1..]).unwrap_or_default()
- && let impl_ty = if cx.tcx.fn_sig(fn_id)
- .instantiate_identity()
- .skip_binder()
- .inputs()[0].is_ref()
- {
- // Trait methods taking `&self`
- sub_ty
- } else {
- // Trait methods taking `self`
- arg_ty
- } && impl_ty.is_ref()
+ .node_args_opt(hir_id)
+ .map(|args| &args[1..])
+ .unwrap_or_default()
+ && let impl_ty =
+ if cx.tcx.fn_sig(fn_id).instantiate_identity().skip_binder().inputs()[0]
+ .is_ref()
+ {
+ // Trait methods taking `&self`
+ sub_ty
+ } else {
+ // Trait methods taking `self`
+ arg_ty
+ }
+ && impl_ty.is_ref()
&& implements_trait(
cx,
impl_ty,
@@ -414,9 +417,9 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> {
let (required_refs, msg) = if can_auto_borrow {
(1, if deref_count == 1 { borrow_msg } else { deref_msg })
} else if let Some(&Adjustment {
- kind: Adjust::Borrow(AutoBorrow::Ref(_, mutability)),
- ..
- }) = next_adjust
+ kind: Adjust::Borrow(AutoBorrow::Ref(_, mutability)),
+ ..
+ }) = next_adjust
&& matches!(mutability, AutoBorrowMutability::Mut { .. })
&& !stability.is_reborrow_stable()
{
@@ -701,13 +704,15 @@ fn deref_method_same_type<'tcx>(result_ty: Ty<'tcx>, arg_ty: Ty<'tcx>) -> bool {
fn in_postfix_position<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> bool {
if let Some(parent) = get_parent_expr(cx, e)
- && parent.span.ctxt() == e.span.ctxt()
+ && parent.span.eq_ctxt(e.span)
{
match parent.kind {
ExprKind::Call(child, _) | ExprKind::MethodCall(_, child, _, _) | ExprKind::Index(child, _, _)
- if child.hir_id == e.hir_id => true,
- ExprKind::Match(.., MatchSource::TryDesugar(_) | MatchSource::AwaitDesugar)
- | ExprKind::Field(_, _) => true,
+ if child.hir_id == e.hir_id =>
+ {
+ true
+ },
+ ExprKind::Match(.., MatchSource::TryDesugar(_) | MatchSource::AwaitDesugar) | ExprKind::Field(_, _) => true,
_ => false,
}
} else {
@@ -842,8 +847,8 @@ impl TyCoercionStability {
| ty::Adt(..)
| ty::Foreign(_)
| ty::FnDef(..)
- | ty::Generator(..)
- | ty::GeneratorWitness(..)
+ | ty::Coroutine(..)
+ | ty::CoroutineWitness(..)
| ty::Closure(..)
| ty::Never
| ty::Tuple(_)
diff --git a/src/tools/clippy/clippy_lints/src/derivable_impls.rs b/src/tools/clippy/clippy_lints/src/derivable_impls.rs
index d2bfc4f8e..a450becc6 100644
--- a/src/tools/clippy/clippy_lints/src/derivable_impls.rs
+++ b/src/tools/clippy/clippy_lints/src/derivable_impls.rs
@@ -1,5 +1,5 @@
+use clippy_config::msrvs::{self, Msrv};
use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::source::indent_of;
use clippy_utils::{is_default_equivalent, peel_blocks};
use rustc_errors::Applicability;
@@ -21,7 +21,7 @@ declare_clippy_lint! {
/// It is less concise.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// struct Foo {
/// bar: bool
/// }
@@ -36,7 +36,7 @@ declare_clippy_lint! {
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// #[derive(Default)]
/// struct Foo {
/// bar: bool
diff --git a/src/tools/clippy/clippy_lints/src/derive.rs b/src/tools/clippy/clippy_lints/src/derive.rs
index 2bdac1352..3a331564d 100644
--- a/src/tools/clippy/clippy_lints/src/derive.rs
+++ b/src/tools/clippy/clippy_lints/src/derive.rs
@@ -17,8 +17,7 @@ use rustc_middle::ty::{
};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::def_id::LocalDefId;
-use rustc_span::source_map::Span;
-use rustc_span::sym;
+use rustc_span::{sym, Span};
declare_clippy_lint! {
/// ### What it does
@@ -173,7 +172,7 @@ declare_clippy_lint! {
/// `Eq` themselves.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// #[derive(PartialEq)]
/// struct Foo {
/// i_am_eq: i32,
@@ -181,7 +180,7 @@ declare_clippy_lint! {
/// }
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// #[derive(PartialEq, Eq)]
/// struct Foo {
/// i_am_eq: i32,
diff --git a/src/tools/clippy/clippy_lints/src/disallowed_macros.rs b/src/tools/clippy/clippy_lints/src/disallowed_macros.rs
index 7469f813e..324b5e079 100644
--- a/src/tools/clippy/clippy_lints/src/disallowed_macros.rs
+++ b/src/tools/clippy/clippy_lints/src/disallowed_macros.rs
@@ -1,3 +1,4 @@
+use clippy_config::types::DisallowedPath;
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::macros::macro_backtrace;
use rustc_ast::Attribute;
@@ -8,8 +9,6 @@ use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::{ExpnId, Span};
-use crate::utils::conf;
-
declare_clippy_lint! {
/// ### What it does
/// Denies the configured macros in clippy.toml
@@ -35,7 +34,7 @@ declare_clippy_lint! {
/// { path = "serde::Serialize", reason = "no serializing" },
/// ]
/// ```
- /// ```
+ /// ```no_run
/// use serde::Serialize;
///
/// // Example code where clippy issues a warning
@@ -55,13 +54,13 @@ declare_clippy_lint! {
}
pub struct DisallowedMacros {
- conf_disallowed: Vec<conf::DisallowedPath>,
+ conf_disallowed: Vec<DisallowedPath>,
disallowed: DefIdMap<usize>,
seen: FxHashSet<ExpnId>,
}
impl DisallowedMacros {
- pub fn new(conf_disallowed: Vec<conf::DisallowedPath>) -> Self {
+ pub fn new(conf_disallowed: Vec<DisallowedPath>) -> Self {
Self {
conf_disallowed,
disallowed: DefIdMap::default(),
diff --git a/src/tools/clippy/clippy_lints/src/disallowed_methods.rs b/src/tools/clippy/clippy_lints/src/disallowed_methods.rs
index 95d3f7547..d23aeebb5 100644
--- a/src/tools/clippy/clippy_lints/src/disallowed_methods.rs
+++ b/src/tools/clippy/clippy_lints/src/disallowed_methods.rs
@@ -1,13 +1,11 @@
+use clippy_config::types::DisallowedPath;
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::{fn_def_id, get_parent_expr, path_def_id};
-
use rustc_hir::def_id::DefIdMap;
use rustc_hir::{Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_tool_lint, impl_lint_pass};
-use crate::utils::conf;
-
declare_clippy_lint! {
/// ### What it does
/// Denies the configured methods and functions in clippy.toml
@@ -59,12 +57,12 @@ declare_clippy_lint! {
#[derive(Clone, Debug)]
pub struct DisallowedMethods {
- conf_disallowed: Vec<conf::DisallowedPath>,
+ conf_disallowed: Vec<DisallowedPath>,
disallowed: DefIdMap<usize>,
}
impl DisallowedMethods {
- pub fn new(conf_disallowed: Vec<conf::DisallowedPath>) -> Self {
+ pub fn new(conf_disallowed: Vec<DisallowedPath>) -> Self {
Self {
conf_disallowed,
disallowed: DefIdMap::default(),
diff --git a/src/tools/clippy/clippy_lints/src/disallowed_names.rs b/src/tools/clippy/clippy_lints/src/disallowed_names.rs
index 04c2d4413..5e46b29b6 100644
--- a/src/tools/clippy/clippy_lints/src/disallowed_names.rs
+++ b/src/tools/clippy/clippy_lints/src/disallowed_names.rs
@@ -15,7 +15,7 @@ declare_clippy_lint! {
/// avoided.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let foo = 3.14;
/// ```
#[clippy::version = "pre 1.29.0"]
diff --git a/src/tools/clippy/clippy_lints/src/disallowed_script_idents.rs b/src/tools/clippy/clippy_lints/src/disallowed_script_idents.rs
index c9fad98e4..96a7f0e4f 100644
--- a/src/tools/clippy/clippy_lints/src/disallowed_script_idents.rs
+++ b/src/tools/clippy/clippy_lints/src/disallowed_script_idents.rs
@@ -30,7 +30,7 @@ declare_clippy_lint! {
/// [`non_ascii_idents`]: https://doc.rust-lang.org/rustc/lints/listing/allowed-by-default.html#non-ascii-idents
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// // Assuming that `clippy.toml` contains the following line:
/// // allowed-scripts = ["Latin", "Cyrillic"]
/// let counter = 10; // OK, latin is allowed.
diff --git a/src/tools/clippy/clippy_lints/src/disallowed_types.rs b/src/tools/clippy/clippy_lints/src/disallowed_types.rs
index 1f56d0118..3578fb640 100644
--- a/src/tools/clippy/clippy_lints/src/disallowed_types.rs
+++ b/src/tools/clippy/clippy_lints/src/disallowed_types.rs
@@ -1,5 +1,5 @@
+use clippy_config::types::DisallowedPath;
use clippy_utils::diagnostics::span_lint_and_then;
-
use rustc_data_structures::fx::FxHashMap;
use rustc_hir::def::Res;
use rustc_hir::def_id::DefId;
@@ -8,8 +8,6 @@ use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::Span;
-use crate::utils::conf;
-
declare_clippy_lint! {
/// ### What it does
/// Denies the configured types in clippy.toml.
@@ -50,15 +48,16 @@ declare_clippy_lint! {
style,
"use of disallowed types"
}
+
#[derive(Clone, Debug)]
pub struct DisallowedTypes {
- conf_disallowed: Vec<conf::DisallowedPath>,
+ conf_disallowed: Vec<DisallowedPath>,
def_ids: FxHashMap<DefId, usize>,
prim_tys: FxHashMap<PrimTy, usize>,
}
impl DisallowedTypes {
- pub fn new(conf_disallowed: Vec<conf::DisallowedPath>) -> Self {
+ pub fn new(conf_disallowed: Vec<DisallowedPath>) -> Self {
Self {
conf_disallowed,
def_ids: FxHashMap::default(),
@@ -123,7 +122,7 @@ impl<'tcx> LateLintPass<'tcx> for DisallowedTypes {
}
}
-fn emit(cx: &LateContext<'_>, name: &str, span: Span, conf: &conf::DisallowedPath) {
+fn emit(cx: &LateContext<'_>, name: &str, span: Span, conf: &DisallowedPath) {
span_lint_and_then(
cx,
DISALLOWED_TYPES,
diff --git a/src/tools/clippy/clippy_lints/src/doc.rs b/src/tools/clippy/clippy_lints/src/doc.rs
index e789e0da6..8982fca6e 100644
--- a/src/tools/clippy/clippy_lints/src/doc.rs
+++ b/src/tools/clippy/clippy_lints/src/doc.rs
@@ -30,8 +30,8 @@ use rustc_resolve::rustdoc::{
use rustc_session::parse::ParseSess;
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::edition::Edition;
-use rustc_span::source_map::{BytePos, FilePathMapping, SourceMap, Span};
-use rustc_span::{sym, FileName, Pos};
+use rustc_span::{sym, BytePos, FileName, Pos, Span};
+use rustc_span::source_map::{FilePathMapping, SourceMap};
use std::ops::Range;
use std::{io, thread};
use url::Url;
@@ -58,14 +58,14 @@ declare_clippy_lint! {
/// would fail.
///
/// ### Examples
- /// ```rust
+ /// ```no_run
/// /// Do something with the foo_bar parameter. See also
/// /// that::other::module::foo.
/// // ^ `foo_bar` and `that::other::module::foo` should be ticked.
/// fn doit(foo_bar: usize) {}
/// ```
///
- /// ```rust
+ /// ```no_run
/// // Link text with `[]` brackets should be written as following:
/// /// Consume the array and return the inner
/// /// [`SmallVec<[T; INLINE_CAPACITY]>`][SmallVec].
@@ -88,7 +88,7 @@ declare_clippy_lint! {
/// preconditions, so that users can be sure they are using them safely.
///
/// ### Examples
- /// ```rust
+ /// ```no_run
///# type Universe = ();
/// /// This function should really be documented
/// pub unsafe fn start_apocalypse(u: &mut Universe) {
@@ -98,7 +98,7 @@ declare_clippy_lint! {
///
/// At least write a line about safety:
///
- /// ```rust
+ /// ```no_run
///# type Universe = ();
/// /// # Safety
/// ///
@@ -126,7 +126,7 @@ declare_clippy_lint! {
/// Since the following function returns a `Result` it has an `# Errors` section in
/// its doc comment:
///
- /// ```rust
+ /// ```no_run
///# use std::io;
/// /// # Errors
/// ///
@@ -155,7 +155,7 @@ declare_clippy_lint! {
/// Since the following function may panic it has a `# Panics` section in
/// its doc comment:
///
- /// ```rust
+ /// ```no_run
/// /// # Panics
/// ///
/// /// Will panic if y is 0
@@ -182,7 +182,7 @@ declare_clippy_lint! {
/// if the `fn main()` is left implicit.
///
/// ### Examples
- /// ```rust
+ /// ```no_run
/// /// An example of a doctest with a `main()` function
/// ///
/// /// # Examples
@@ -210,12 +210,12 @@ declare_clippy_lint! {
/// It is likely a typo when defining an intra-doc link
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// /// See also: ['foo']
/// fn bar() {}
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// /// See also: [`foo`]
/// fn bar() {}
/// ```
@@ -235,7 +235,7 @@ declare_clippy_lint! {
/// need to describe safety preconditions that users are required to uphold.
///
/// ### Examples
- /// ```rust
+ /// ```no_run
///# type Universe = ();
/// /// # Safety
/// ///
@@ -248,7 +248,7 @@ declare_clippy_lint! {
/// The function is safe, so there shouldn't be any preconditions
/// that have to be explained for safety reasons.
///
- /// ```rust
+ /// ```no_run
///# type Universe = ();
/// /// This function should really be documented
/// pub fn start_apocalypse(u: &mut Universe) {
@@ -436,8 +436,8 @@ fn lint_for_missing_headers(
let body = cx.tcx.hir().body(body_id);
let ret_ty = typeck.expr_ty(body.value);
if implements_trait(cx, ret_ty, future, &[]);
- if let ty::Generator(_, subs, _) = ret_ty.kind();
- if is_type_diagnostic_item(cx, subs.as_generator().return_ty(), sym::Result);
+ if let ty::Coroutine(_, subs, _) = ret_ty.kind();
+ if is_type_diagnostic_item(cx, subs.as_coroutine().return_ty(), sym::Result);
then {
span_lint(
cx,
@@ -569,9 +569,7 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
if let End(Heading(_, _, _)) = event {
in_heading = false;
}
- if ticks_unbalanced
- && let Some(span) = fragments.span(cx, paragraph_range.clone())
- {
+ if ticks_unbalanced && let Some(span) = fragments.span(cx, paragraph_range.clone()) {
span_lint_and_help(
cx,
DOC_MARKDOWN,
@@ -617,8 +615,9 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
check_link_quotes(cx, trimmed_text, range.clone(), fragments);
}
if let Some(link) = in_link.as_ref()
- && let Ok(url) = Url::parse(link)
- && (url.scheme() == "https" || url.scheme() == "http") {
+ && let Ok(url) = Url::parse(link)
+ && (url.scheme() == "https" || url.scheme() == "http")
+ {
// Don't check the text associated with external URLs
continue;
}
@@ -716,7 +715,9 @@ fn check_code(cx: &LateContext<'_>, text: &str, edition: Edition, range: Range<u
// Because of the global session, we need to create a new session in a different thread with
// the edition we need.
let text = text.to_owned();
- if thread::spawn(move || has_needless_main(text, edition)).join().expect("thread::spawn failed")
+ if thread::spawn(move || has_needless_main(text, edition))
+ .join()
+ .expect("thread::spawn failed")
&& let Some(span) = fragments.span(cx, range.start..range.end - trailing_whitespace)
{
span_lint(cx, NEEDLESS_DOCTEST_MAIN, span, "needless `fn main` in doctest");
@@ -756,11 +757,12 @@ fn check_text(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, text: &str
}
fn check_word(cx: &LateContext<'_>, word: &str, span: Span) {
- /// Checks if a string is camel-case, i.e., contains at least two uppercase
- /// letters (`Clippy` is ok) and one lower-case letter (`NASA` is ok).
+ /// Checks if a string is upper-camel-case, i.e., starts with an uppercase and
+ /// contains at least two uppercase letters (`Clippy` is ok) and one lower-case
+ /// letter (`NASA` is ok).
/// Plurals are also excluded (`IDs` is ok).
fn is_camel_case(s: &str) -> bool {
- if s.starts_with(|c: char| c.is_ascii_digit()) {
+ if s.starts_with(|c: char| c.is_ascii_digit() | c.is_ascii_lowercase()) {
return false;
}
diff --git a/src/tools/clippy/clippy_lints/src/double_parens.rs b/src/tools/clippy/clippy_lints/src/double_parens.rs
index 29425b2e5..63f32173b 100644
--- a/src/tools/clippy/clippy_lints/src/double_parens.rs
+++ b/src/tools/clippy/clippy_lints/src/double_parens.rs
@@ -12,7 +12,7 @@ declare_clippy_lint! {
/// mistake.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// fn simple_double_parens() -> i32 {
/// ((0))
/// }
@@ -22,7 +22,7 @@ declare_clippy_lint! {
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// fn simple_no_parens() -> i32 {
/// 0
/// }
diff --git a/src/tools/clippy/clippy_lints/src/drop_forget_ref.rs b/src/tools/clippy/clippy_lints/src/drop_forget_ref.rs
index 14122abbf..177e04dfa 100644
--- a/src/tools/clippy/clippy_lints/src/drop_forget_ref.rs
+++ b/src/tools/clippy/clippy_lints/src/drop_forget_ref.rs
@@ -16,7 +16,7 @@ declare_clippy_lint! {
/// have been intended.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// struct Foo;
/// let x = Foo;
/// std::mem::drop(x);
@@ -36,7 +36,7 @@ declare_clippy_lint! {
/// have been intended.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// struct Foo;
/// let x = Foo;
/// std::mem::forget(x);
@@ -57,7 +57,7 @@ declare_clippy_lint! {
/// destructor, possibly causing leaks.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # use std::mem;
/// # use std::rc::Rc;
/// mem::forget(Rc::new(55))
@@ -90,7 +90,8 @@ impl<'tcx> LateLintPass<'tcx> for DropForgetRef {
let is_copy = is_copy(cx, arg_ty);
let drop_is_single_call_in_arm = is_single_call_in_arm(cx, arg, expr);
let (lint, msg, note_span) = match fn_name {
- // early return for uplifted lints: dropping_references, dropping_copy_types, forgetting_references, forgetting_copy_types
+ // early return for uplifted lints: dropping_references, dropping_copy_types, forgetting_references,
+ // forgetting_copy_types
sym::mem_drop if arg_ty.is_ref() && !drop_is_single_call_in_arm => return,
sym::mem_forget if arg_ty.is_ref() => return,
sym::mem_drop if is_copy && !drop_is_single_call_in_arm => return,
@@ -100,8 +101,7 @@ impl<'tcx> LateLintPass<'tcx> for DropForgetRef {
if !(arg_ty.needs_drop(cx.tcx, cx.param_env)
|| is_must_use_func_call(cx, arg)
|| is_must_use_ty(cx, arg_ty)
- || drop_is_single_call_in_arm
- ) =>
+ || drop_is_single_call_in_arm) =>
{
(DROP_NON_DROP, DROP_NON_DROP_SUMMARY.into(), Some(arg.span))
},
@@ -122,7 +122,7 @@ impl<'tcx> LateLintPass<'tcx> for DropForgetRef {
} else {
(FORGET_NON_DROP, FORGET_NON_DROP_SUMMARY.into(), Some(arg.span))
}
- }
+ },
_ => return,
};
span_lint_and_note(
diff --git a/src/tools/clippy/clippy_lints/src/else_if_without_else.rs b/src/tools/clippy/clippy_lints/src/else_if_without_else.rs
index bf4488570..61db1c1ab 100644
--- a/src/tools/clippy/clippy_lints/src/else_if_without_else.rs
+++ b/src/tools/clippy/clippy_lints/src/else_if_without_else.rs
@@ -15,7 +15,7 @@ declare_clippy_lint! {
/// Some coding guidelines require this (e.g., MISRA-C:2004 Rule 14.10).
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # fn a() {}
/// # fn b() {}
/// # let x: i32 = 1;
@@ -28,7 +28,7 @@ declare_clippy_lint! {
///
/// Use instead:
///
- /// ```rust
+ /// ```no_run
/// # fn a() {}
/// # fn b() {}
/// # let x: i32 = 1;
diff --git a/src/tools/clippy/clippy_lints/src/empty_drop.rs b/src/tools/clippy/clippy_lints/src/empty_drop.rs
index 209fb66fa..5fcdca7cf 100644
--- a/src/tools/clippy/clippy_lints/src/empty_drop.rs
+++ b/src/tools/clippy/clippy_lints/src/empty_drop.rs
@@ -16,7 +16,7 @@ declare_clippy_lint! {
/// destructured, which might be the intention behind adding the implementation as a marker.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// struct S;
///
/// impl Drop for S {
@@ -24,7 +24,7 @@ declare_clippy_lint! {
/// }
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// struct S;
/// ```
#[clippy::version = "1.62.0"]
diff --git a/src/tools/clippy/clippy_lints/src/empty_enum.rs b/src/tools/clippy/clippy_lints/src/empty_enum.rs
index 1701d0611..a5699727b 100644
--- a/src/tools/clippy/clippy_lints/src/empty_enum.rs
+++ b/src/tools/clippy/clippy_lints/src/empty_enum.rs
@@ -23,12 +23,12 @@ declare_clippy_lint! {
///
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// enum Test {}
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// #![feature(never_type)]
///
/// struct Test(!);
diff --git a/src/tools/clippy/clippy_lints/src/empty_structs_with_brackets.rs b/src/tools/clippy/clippy_lints/src/empty_structs_with_brackets.rs
index 282157181..4e2a8b73c 100644
--- a/src/tools/clippy/clippy_lints/src/empty_structs_with_brackets.rs
+++ b/src/tools/clippy/clippy_lints/src/empty_structs_with_brackets.rs
@@ -15,11 +15,11 @@ declare_clippy_lint! {
/// Empty brackets after a struct declaration can be omitted.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// struct Cookie {}
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// struct Cookie;
/// ```
#[clippy::version = "1.62.0"]
@@ -35,7 +35,8 @@ impl EarlyLintPass for EmptyStructsWithBrackets {
if let ItemKind::Struct(var_data, _) = &item.kind
&& has_brackets(var_data)
- && has_no_fields(cx, var_data, span_after_ident) {
+ && has_no_fields(cx, var_data, span_after_ident)
+ {
span_lint_and_then(
cx,
EMPTY_STRUCTS_WITH_BRACKETS,
@@ -46,8 +47,9 @@ impl EarlyLintPass for EmptyStructsWithBrackets {
span_after_ident,
"remove the brackets",
";",
- Applicability::Unspecified);
- },
+ Applicability::Unspecified,
+ );
+ },
);
}
}
diff --git a/src/tools/clippy/clippy_lints/src/entry.rs b/src/tools/clippy/clippy_lints/src/entry.rs
index 6197b5b19..3e3c62e85 100644
--- a/src/tools/clippy/clippy_lints/src/entry.rs
+++ b/src/tools/clippy/clippy_lints/src/entry.rs
@@ -23,7 +23,7 @@ declare_clippy_lint! {
///
/// ### Known problems
/// The suggestion may have type inference errors in some cases. e.g.
- /// ```rust
+ /// ```no_run
/// let mut map = std::collections::HashMap::new();
/// let _ = if !map.contains_key(&0) {
/// map.insert(0, 0)
@@ -33,7 +33,7 @@ declare_clippy_lint! {
/// ```
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # use std::collections::HashMap;
/// # let mut map = HashMap::new();
/// # let k = 1;
@@ -43,7 +43,7 @@ declare_clippy_lint! {
/// }
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # use std::collections::HashMap;
/// # let mut map = HashMap::new();
/// # let k = 1;
@@ -241,7 +241,7 @@ fn try_parse_contains<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'_>) -> Optio
},
],
_,
- ) if key_span.ctxt() == expr.span.ctxt() => {
+ ) if key_span.eq_ctxt(expr.span) => {
let id = cx.typeck_results().type_dependent_def_id(expr.hir_id)?;
let expr = ContainsExpr {
negated,
diff --git a/src/tools/clippy/clippy_lints/src/enum_clike.rs b/src/tools/clippy/clippy_lints/src/enum_clike.rs
index 3f60e5a7c..003b5fc72 100644
--- a/src/tools/clippy/clippy_lints/src/enum_clike.rs
+++ b/src/tools/clippy/clippy_lints/src/enum_clike.rs
@@ -1,7 +1,7 @@
//! lint on C-like enums that are `repr(isize/usize)` and have values that
//! don't fit into an `i32`
-use clippy_utils::consts::{miri_to_const, Constant};
+use clippy_utils::consts::{mir_to_const, Constant};
use clippy_utils::diagnostics::span_lint;
use rustc_hir::{Item, ItemKind};
use rustc_lint::{LateContext, LateLintPass};
@@ -19,7 +19,7 @@ declare_clippy_lint! {
/// architectures, but works fine on 64 bit.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # #[cfg(target_pointer_width = "64")]
/// #[repr(usize)]
/// enum NonPortable {
@@ -51,7 +51,7 @@ impl<'tcx> LateLintPass<'tcx> for UnportableVariant {
.const_eval_poly(def_id.to_def_id())
.ok()
.map(|val| rustc_middle::mir::Const::from_value(val, ty));
- if let Some(Constant::Int(val)) = constant.and_then(|c| miri_to_const(cx, c)) {
+ if let Some(Constant::Int(val)) = constant.and_then(|c| mir_to_const(cx, c)) {
if let ty::Adt(adt, _) = ty.kind() {
if adt.is_enum() {
ty = adt.repr().discr_type().to_ty(cx.tcx);
diff --git a/src/tools/clippy/clippy_lints/src/equatable_if_let.rs b/src/tools/clippy/clippy_lints/src/equatable_if_let.rs
index c691e6c54..575fead5b 100644
--- a/src/tools/clippy/clippy_lints/src/equatable_if_let.rs
+++ b/src/tools/clippy/clippy_lints/src/equatable_if_let.rs
@@ -68,7 +68,8 @@ impl<'tcx> LateLintPass<'tcx> for PatternEquality {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
if !in_external_macro(cx.sess(), expr.span)
&& let ExprKind::Let(let_expr) = expr.kind
- && unary_pattern(let_expr.pat) {
+ && unary_pattern(let_expr.pat)
+ {
let exp_ty = cx.typeck_results().expr_ty(let_expr.init);
let pat_ty = cx.typeck_results().pat_ty(let_expr.pat);
let mut applicability = Applicability::MachineApplicable;
@@ -79,7 +80,9 @@ impl<'tcx> LateLintPass<'tcx> for PatternEquality {
"({})",
snippet_with_context(cx, let_expr.pat.span, expr.span.ctxt(), "..", &mut applicability).0,
),
- _ => snippet_with_context(cx, let_expr.pat.span, expr.span.ctxt(), "..", &mut applicability).0.to_string(),
+ _ => snippet_with_context(cx, let_expr.pat.span, expr.span.ctxt(), "..", &mut applicability)
+ .0
+ .to_string(),
};
span_lint_and_sugg(
cx,
diff --git a/src/tools/clippy/clippy_lints/src/error_impl_error.rs b/src/tools/clippy/clippy_lints/src/error_impl_error.rs
index f24577c73..bc878555c 100644
--- a/src/tools/clippy/clippy_lints/src/error_impl_error.rs
+++ b/src/tools/clippy/clippy_lints/src/error_impl_error.rs
@@ -27,7 +27,7 @@ declare_clippy_lint! {
///
/// impl std::error::Error for Error { ... }
/// ```
- #[clippy::version = "1.72.0"]
+ #[clippy::version = "1.73.0"]
pub ERROR_IMPL_ERROR,
restriction,
"exported types named `Error` that implement `Error`"
@@ -41,10 +41,11 @@ impl<'tcx> LateLintPass<'tcx> for ErrorImplError {
};
match item.kind {
- ItemKind::TyAlias(..) if item.ident.name == sym::Error
- && is_visible_outside_module(cx, item.owner_id.def_id)
- && let ty = cx.tcx.type_of(item.owner_id).instantiate_identity()
- && implements_trait(cx, ty, error_def_id, &[]) =>
+ ItemKind::TyAlias(..)
+ if item.ident.name == sym::Error
+ && is_visible_outside_module(cx, item.owner_id.def_id)
+ && let ty = cx.tcx.type_of(item.owner_id).instantiate_identity()
+ && implements_trait(cx, ty, error_def_id, &[]) =>
{
span_lint(
cx,
@@ -53,13 +54,14 @@ impl<'tcx> LateLintPass<'tcx> for ErrorImplError {
"exported type alias named `Error` that implements `Error`",
);
},
- ItemKind::Impl(imp) if let Some(trait_def_id) = imp.of_trait.and_then(|t| t.trait_def_id())
- && error_def_id == trait_def_id
- && let Some(def_id) = path_res(cx, imp.self_ty).opt_def_id().and_then(DefId::as_local)
- && let hir_id = cx.tcx.hir().local_def_id_to_hir_id(def_id)
- && let Some(ident) = cx.tcx.opt_item_ident(def_id.to_def_id())
- && ident.name == sym::Error
- && is_visible_outside_module(cx, def_id) =>
+ ItemKind::Impl(imp)
+ if let Some(trait_def_id) = imp.of_trait.and_then(|t| t.trait_def_id())
+ && error_def_id == trait_def_id
+ && let Some(def_id) = path_res(cx, imp.self_ty).opt_def_id().and_then(DefId::as_local)
+ && let hir_id = cx.tcx.hir().local_def_id_to_hir_id(def_id)
+ && let Some(ident) = cx.tcx.opt_item_ident(def_id.to_def_id())
+ && ident.name == sym::Error
+ && is_visible_outside_module(cx, def_id) =>
{
span_lint_hir_and_then(
cx,
@@ -69,9 +71,9 @@ impl<'tcx> LateLintPass<'tcx> for ErrorImplError {
"exported type named `Error` that implements `Error`",
|diag| {
diag.span_note(item.span, "`Error` was implemented here");
- }
+ },
);
- }
+ },
_ => {},
}
}
diff --git a/src/tools/clippy/clippy_lints/src/escape.rs b/src/tools/clippy/clippy_lints/src/escape.rs
index dbe3453e7..3d0ddca19 100644
--- a/src/tools/clippy/clippy_lints/src/escape.rs
+++ b/src/tools/clippy/clippy_lints/src/escape.rs
@@ -8,7 +8,7 @@ use rustc_middle::ty::layout::LayoutOf;
use rustc_middle::ty::{self, TraitRef, Ty};
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::def_id::LocalDefId;
-use rustc_span::source_map::Span;
+use rustc_span::Span;
use rustc_span::symbol::kw;
use rustc_target::spec::abi::Abi;
@@ -28,12 +28,12 @@ declare_clippy_lint! {
/// into something.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// fn foo(x: Box<u32>) {}
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// fn foo(x: u32) {}
/// ```
#[clippy::version = "pre 1.29.0"]
diff --git a/src/tools/clippy/clippy_lints/src/eta_reduction.rs b/src/tools/clippy/clippy_lints/src/eta_reduction.rs
index 38066503c..fad8fbf04 100644
--- a/src/tools/clippy/clippy_lints/src/eta_reduction.rs
+++ b/src/tools/clippy/clippy_lints/src/eta_reduction.rs
@@ -119,19 +119,21 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction {
match body.value.kind {
ExprKind::Call(callee, args)
- if matches!(callee.kind, ExprKind::Path(QPath::Resolved(..) | QPath::TypeRelative(..))) =>
+ if matches!(
+ callee.kind,
+ ExprKind::Path(QPath::Resolved(..) | QPath::TypeRelative(..))
+ ) =>
{
let callee_ty = typeck.expr_ty(callee).peel_refs();
- if matches!(
- type_diagnostic_name(cx, callee_ty),
- Some(sym::Arc | sym::Rc)
- ) || !check_inputs(typeck, body.params, None, args) {
+ if matches!(type_diagnostic_name(cx, callee_ty), Some(sym::Arc | sym::Rc))
+ || !check_inputs(typeck, body.params, None, args)
+ {
return;
}
- let callee_ty_adjusted = typeck.expr_adjustments(callee).last().map_or(
- callee_ty,
- |a| a.target.peel_refs(),
- );
+ let callee_ty_adjusted = typeck
+ .expr_adjustments(callee)
+ .last()
+ .map_or(callee_ty, |a| a.target.peel_refs());
let sig = match callee_ty_adjusted.kind() {
ty::FnDef(def, _) => cx.tcx.fn_sig(def).skip_binder().skip_binder(),
@@ -160,36 +162,26 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction {
// For now ignore all callee types which reference a type parameter.
&& !generic_args.types().any(|t| matches!(t.kind(), ty::Param(_)))
{
- span_lint_and_then(
- cx,
- REDUNDANT_CLOSURE,
- expr.span,
- "redundant closure",
- |diag| {
- if let Some(mut snippet) = snippet_opt(cx, callee.span) {
- if let Ok((ClosureKind::FnMut, _))
- = cx.tcx.infer_ctxt().build().type_implements_fn_trait(
- cx.param_env,
- Binder::bind_with_vars(callee_ty_adjusted, List::empty()),
- ImplPolarity::Positive,
- ) && path_to_local(callee)
- .map_or(
- false,
- |l| local_used_in(cx, l, args) || local_used_after_expr(cx, l, expr),
- )
- {
- // Mutable closure is used after current expr; we cannot consume it.
- snippet = format!("&mut {snippet}");
- }
- diag.span_suggestion(
- expr.span,
- "replace the closure with the function itself",
- snippet,
- Applicability::MachineApplicable,
- );
+ span_lint_and_then(cx, REDUNDANT_CLOSURE, expr.span, "redundant closure", |diag| {
+ if let Some(mut snippet) = snippet_opt(cx, callee.span) {
+ if let Ok((ClosureKind::FnMut, _)) = cx.tcx.infer_ctxt().build().type_implements_fn_trait(
+ cx.param_env,
+ Binder::bind_with_vars(callee_ty_adjusted, List::empty()),
+ ImplPolarity::Positive,
+ ) && path_to_local(callee).map_or(false, |l| {
+ local_used_in(cx, l, args) || local_used_after_expr(cx, l, expr)
+ }) {
+ // Mutable closure is used after current expr; we cannot consume it.
+ snippet = format!("&mut {snippet}");
}
+ diag.span_suggestion(
+ expr.span,
+ "replace the closure with the function itself",
+ snippet,
+ Applicability::MachineApplicable,
+ );
}
- );
+ });
}
},
ExprKind::MethodCall(path, self_, args, _) if check_inputs(typeck, body.params, Some(self_), args) => {
diff --git a/src/tools/clippy/clippy_lints/src/excessive_bools.rs b/src/tools/clippy/clippy_lints/src/excessive_bools.rs
index aef2db385..1d18e194d 100644
--- a/src/tools/clippy/clippy_lints/src/excessive_bools.rs
+++ b/src/tools/clippy/clippy_lints/src/excessive_bools.rs
@@ -22,7 +22,7 @@ declare_clippy_lint! {
/// readability and API.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// struct S {
/// is_pending: bool,
/// is_processing: bool,
@@ -31,7 +31,7 @@ declare_clippy_lint! {
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// enum S {
/// Pending,
/// Processing,
@@ -157,7 +157,7 @@ impl<'tcx> LateLintPass<'tcx> for ExcessiveBools {
// functions with a body are already checked by `check_fn`
if let TraitItemKind::Fn(fn_sig, TraitFn::Required(_)) = &trait_item.kind
&& fn_sig.header.abi == Abi::Rust
- {
+ {
self.check_fn_sig(cx, fn_sig.decl, fn_sig.span);
}
}
@@ -174,11 +174,8 @@ impl<'tcx> LateLintPass<'tcx> for ExcessiveBools {
let hir_id = cx.tcx.hir().local_def_id_to_hir_id(def_id);
if let Some(fn_header) = fn_kind.header()
&& fn_header.abi == Abi::Rust
- && get_parent_as_impl(cx.tcx, hir_id)
- .map_or(true,
- |impl_item| impl_item.of_trait.is_none()
- )
- {
+ && get_parent_as_impl(cx.tcx, hir_id).map_or(true, |impl_item| impl_item.of_trait.is_none())
+ {
self.check_fn_sig(cx, fn_decl, span);
}
}
diff --git a/src/tools/clippy/clippy_lints/src/exhaustive_items.rs b/src/tools/clippy/clippy_lints/src/exhaustive_items.rs
index 9fd13084d..f976cfd3f 100644
--- a/src/tools/clippy/clippy_lints/src/exhaustive_items.rs
+++ b/src/tools/clippy/clippy_lints/src/exhaustive_items.rs
@@ -17,14 +17,14 @@ declare_clippy_lint! {
/// disable them by default.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// enum Foo {
/// Bar,
/// Baz
/// }
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// #[non_exhaustive]
/// enum Foo {
/// Bar,
@@ -47,14 +47,14 @@ declare_clippy_lint! {
/// disable them by default.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// struct Foo {
/// bar: u8,
/// baz: String,
/// }
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// #[non_exhaustive]
/// struct Foo {
/// bar: u8,
diff --git a/src/tools/clippy/clippy_lints/src/exit.rs b/src/tools/clippy/clippy_lints/src/exit.rs
index 8ba6a9e48..e14b1c556 100644
--- a/src/tools/clippy/clippy_lints/src/exit.rs
+++ b/src/tools/clippy/clippy_lints/src/exit.rs
@@ -1,9 +1,10 @@
use clippy_utils::diagnostics::span_lint;
-use clippy_utils::{is_entrypoint_fn, match_def_path, paths};
+use clippy_utils::is_entrypoint_fn;
use if_chain::if_chain;
use rustc_hir::{Expr, ExprKind, Item, ItemKind, Node};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::sym;
declare_clippy_lint! {
/// ### What it does
@@ -16,7 +17,7 @@ declare_clippy_lint! {
/// the main function.
///
/// ### Example
- /// ```
+ /// ```no_run
/// std::process::exit(0)
/// ```
///
@@ -45,7 +46,7 @@ impl<'tcx> LateLintPass<'tcx> for Exit {
if let ExprKind::Call(path_expr, _args) = e.kind;
if let ExprKind::Path(ref path) = path_expr.kind;
if let Some(def_id) = cx.qpath_res(path, path_expr.hir_id).opt_def_id();
- if match_def_path(cx, def_id, &paths::EXIT);
+ if cx.tcx.is_diagnostic_item(sym::process_exit, def_id);
let parent = cx.tcx.hir().get_parent_item(e.hir_id).def_id;
if let Some(Node::Item(Item{kind: ItemKind::Fn(..), ..})) = cx.tcx.hir().find_by_def_id(parent);
// If the next item up is a function we check if it is an entry point
diff --git a/src/tools/clippy/clippy_lints/src/explicit_write.rs b/src/tools/clippy/clippy_lints/src/explicit_write.rs
index b612cc00b..4b5bcb06a 100644
--- a/src/tools/clippy/clippy_lints/src/explicit_write.rs
+++ b/src/tools/clippy/clippy_lints/src/explicit_write.rs
@@ -1,7 +1,7 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::macros::{find_format_args, format_args_inputs_span};
use clippy_utils::source::snippet_with_applicability;
-use clippy_utils::{is_expn_of, match_function_call, paths};
+use clippy_utils::{is_expn_of, path_def_id};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::def::Res;
@@ -19,7 +19,7 @@ declare_clippy_lint! {
/// Using `(e)println! is clearer and more concise
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # use std::io::Write;
/// # let bar = "furchtbar";
/// writeln!(&mut std::io::stderr(), "foo: {:?}", bar).unwrap();
@@ -27,7 +27,7 @@ declare_clippy_lint! {
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # use std::io::Write;
/// # let bar = "furchtbar";
/// eprintln!("foo: {:?}", bar);
@@ -47,18 +47,21 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitWrite {
if let ExprKind::MethodCall(unwrap_fun, write_call, [], _) = expr.kind
&& unwrap_fun.ident.name == sym::unwrap
// match call to write_fmt
- && let ExprKind::MethodCall(write_fun, write_recv, [write_arg], _) = look_in_block(cx, &write_call.kind)
+ && let ExprKind::MethodCall(write_fun, write_recv, [write_arg], _) = *look_in_block(cx, &write_call.kind)
+ && let ExprKind::Call(write_recv_path, _) = write_recv.kind
&& write_fun.ident.name == sym!(write_fmt)
- // match calls to std::io::stdout() / std::io::stderr ()
- && let Some(dest_name) = if match_function_call(cx, write_recv, &paths::STDOUT).is_some() {
- Some("stdout")
- } else if match_function_call(cx, write_recv, &paths::STDERR).is_some() {
- Some("stderr")
- } else {
- None
- }
- && let Some(format_args) = find_format_args(cx, write_arg, ExpnId::root())
+ && let Some(def_id) = path_def_id(cx, write_recv_path)
{
+ // match calls to std::io::stdout() / std::io::stderr ()
+ let (dest_name, prefix) = match cx.tcx.get_diagnostic_name(def_id) {
+ Some(sym::io_stdout) => ("stdout", ""),
+ Some(sym::io_stderr) => ("stderr", "e"),
+ _ => return,
+ };
+ let Some(format_args) = find_format_args(cx, write_arg, ExpnId::root()) else {
+ return;
+ };
+
// ordering is important here, since `writeln!` uses `write!` internally
let calling_macro = if is_expn_of(write_call.span, "writeln").is_some() {
Some("writeln")
@@ -67,11 +70,6 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitWrite {
} else {
None
};
- let prefix = if dest_name == "stderr" {
- "e"
- } else {
- ""
- };
// We need to remove the last trailing newline from the string because the
// underlying `fmt::write` function doesn't know whether `println!` or `print!` was
@@ -82,18 +80,11 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitWrite {
macro_name.replace("write", "print"),
)
} else {
- (
- format!("{dest_name}().write_fmt(...)"),
- "print".into(),
- )
+ (format!("{dest_name}().write_fmt(...)"), "print".into())
};
let mut applicability = Applicability::MachineApplicable;
- let inputs_snippet = snippet_with_applicability(
- cx,
- format_args_inputs_span(&format_args),
- "..",
- &mut applicability,
- );
+ let inputs_snippet =
+ snippet_with_applicability(cx, format_args_inputs_span(&format_args), "..", &mut applicability);
span_lint_and_sugg(
cx,
EXPLICIT_WRITE,
diff --git a/src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs b/src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs
index 0a885984a..d6c746901 100644
--- a/src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs
+++ b/src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs
@@ -23,13 +23,13 @@ declare_clippy_lint! {
/// requires using a turbofish, which serves no purpose but to satisfy the compiler.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// fn unused_ty<T>(x: u8) {
/// // ..
/// }
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// fn no_unused_ty(x: u8) {
/// // ..
/// }
@@ -177,20 +177,22 @@ impl<'cx, 'tcx> TypeWalker<'cx, 'tcx> {
.iter()
.rev()
.map(|(idx, param)| {
- if let Some(next) = explicit_params.get(idx + 1) && end != Some(next.def_id) {
- // Extend the current span forward, up until the next param in the list.
- param.span.until(next.span)
- } else {
- // Extend the current span back to include the comma following the previous
- // param. If the span of the next param in the list has already been
- // extended, we continue the chain. This is why we're iterating in reverse.
- end = Some(param.def_id);
+ if let Some(next) = explicit_params.get(idx + 1)
+ && end != Some(next.def_id)
+ {
+ // Extend the current span forward, up until the next param in the list.
+ param.span.until(next.span)
+ } else {
+ // Extend the current span back to include the comma following the previous
+ // param. If the span of the next param in the list has already been
+ // extended, we continue the chain. This is why we're iterating in reverse.
+ end = Some(param.def_id);
- // idx will never be 0, else we'd be removing the entire list of generics
- let prev = explicit_params[idx - 1];
- let prev_span = self.get_bound_span(prev);
- self.get_bound_span(param).with_lo(prev_span.hi())
- }
+ // idx will never be 0, else we'd be removing the entire list of generics
+ let prev = explicit_params[idx - 1];
+ let prev_span = self.get_bound_span(prev);
+ self.get_bound_span(param).with_lo(prev_span.hi())
+ }
})
.collect()
};
diff --git a/src/tools/clippy/clippy_lints/src/fallible_impl_from.rs b/src/tools/clippy/clippy_lints/src/fallible_impl_from.rs
index 2ef547526..efb69476b 100644
--- a/src/tools/clippy/clippy_lints/src/fallible_impl_from.rs
+++ b/src/tools/clippy/clippy_lints/src/fallible_impl_from.rs
@@ -17,7 +17,7 @@ declare_clippy_lint! {
/// `TryFrom` should be used if there's a possibility of failure.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// struct Foo(i32);
///
/// impl From<String> for Foo {
@@ -28,7 +28,7 @@ declare_clippy_lint! {
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// struct Foo(i32);
///
/// impl TryFrom<String> for Foo {
diff --git a/src/tools/clippy/clippy_lints/src/float_literal.rs b/src/tools/clippy/clippy_lints/src/float_literal.rs
index d182bb621..506a11917 100644
--- a/src/tools/clippy/clippy_lints/src/float_literal.rs
+++ b/src/tools/clippy/clippy_lints/src/float_literal.rs
@@ -18,13 +18,13 @@ declare_clippy_lint! {
/// Rust will truncate the literal silently.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let v: f32 = 0.123_456_789_9;
/// println!("{}", v); // 0.123_456_789
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// let v: f64 = 0.123_456_789_9;
/// println!("{}", v); // 0.123_456_789_9
/// ```
@@ -44,12 +44,12 @@ declare_clippy_lint! {
/// conversion to a float.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let _: f32 = 16_777_217.0; // 16_777_216.0
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// let _: f32 = 16_777_216.0;
/// let _: f64 = 16_777_217.0;
/// ```
diff --git a/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs b/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs
index 29e5315f8..09a9d9924 100644
--- a/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs
+++ b/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs
@@ -27,7 +27,7 @@ declare_clippy_lint! {
/// Negatively impacts accuracy.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let a = 3f32;
/// let _ = a.powf(1.0 / 3.0);
/// let _ = (1.0 + a).ln();
@@ -35,7 +35,7 @@ declare_clippy_lint! {
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// let a = 3f32;
/// let _ = a.cbrt();
/// let _ = a.ln_1p();
@@ -57,7 +57,7 @@ declare_clippy_lint! {
/// Negatively impacts accuracy and performance.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// use std::f32::consts::E;
///
/// let a = 3f32;
@@ -83,7 +83,7 @@ declare_clippy_lint! {
///
/// is better expressed as
///
- /// ```rust
+ /// ```no_run
/// use std::f32::consts::E;
///
/// let a = 3f32;
@@ -323,9 +323,9 @@ fn check_powi(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args:
let maybe_neg_sugg = |expr, hir_id| {
let sugg = Sugg::hir(cx, expr, "..");
if matches!(op, BinOpKind::Sub) && hir_id == rhs.hir_id {
- format!("-{}", sugg.maybe_par())
+ -sugg
} else {
- sugg.to_string()
+ sugg
}
};
@@ -470,25 +470,13 @@ fn check_mul_add(cx: &LateContext<'_>, expr: &Expr<'_>) {
let maybe_neg_sugg = |expr| {
let sugg = Sugg::hir(cx, expr, "..");
- if let BinOpKind::Sub = op {
- format!("-{sugg}")
- } else {
- sugg.to_string()
- }
+ if let BinOpKind::Sub = op { -sugg } else { sugg }
};
let (recv, arg1, arg2) = if let Some((inner_lhs, inner_rhs)) = is_float_mul_expr(cx, lhs) {
- (
- inner_lhs,
- Sugg::hir(cx, inner_rhs, "..").to_string(),
- maybe_neg_sugg(rhs),
- )
+ (inner_lhs, Sugg::hir(cx, inner_rhs, ".."), maybe_neg_sugg(rhs))
} else if let Some((inner_lhs, inner_rhs)) = is_float_mul_expr(cx, rhs) {
- (
- inner_lhs,
- maybe_neg_sugg(inner_rhs),
- Sugg::hir(cx, lhs, "..").to_string(),
- )
+ (inner_lhs, maybe_neg_sugg(inner_rhs), Sugg::hir(cx, lhs, ".."))
} else {
return;
};
diff --git a/src/tools/clippy/clippy_lints/src/format.rs b/src/tools/clippy/clippy_lints/src/format.rs
index b748d3293..18ed05c1c 100644
--- a/src/tools/clippy/clippy_lints/src/format.rs
+++ b/src/tools/clippy/clippy_lints/src/format.rs
@@ -23,13 +23,13 @@ declare_clippy_lint! {
/// if `foo: &str`.
///
/// ### Examples
- /// ```rust
+ /// ```no_run
/// let foo = "foo";
/// format!("{}", foo);
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// let foo = "foo";
/// foo.to_owned();
/// ```
@@ -54,7 +54,9 @@ impl<'tcx> LateLintPass<'tcx> for UselessFormat {
([], []) => span_useless_format_empty(cx, call_site, "String::new()".to_owned(), applicability),
([], [_]) => {
// Simulate macro expansion, converting {{ and }} to { and }.
- let Some(snippet) = snippet_opt(cx, format_args.span) else { return };
+ let Some(snippet) = snippet_opt(cx, format_args.span) else {
+ return;
+ };
let s_expand = snippet.replace("{{", "{").replace("}}", "}");
let sugg = format!("{s_expand}.to_string()");
span_useless_format(cx, call_site, sugg, applicability);
@@ -76,13 +78,14 @@ impl<'tcx> LateLintPass<'tcx> for UselessFormat {
_ => false,
};
let sugg = if is_new_string {
- snippet_with_context(cx, value.span, call_site.ctxt(), "..", &mut applicability).0.into_owned()
+ snippet_with_context(cx, value.span, call_site.ctxt(), "..", &mut applicability)
+ .0
+ .into_owned()
} else {
let sugg = Sugg::hir_with_context(cx, value, call_site.ctxt(), "<arg>", &mut applicability);
format!("{}.to_string()", sugg.maybe_par())
};
span_useless_format(cx, call_site, sugg, applicability);
-
}
},
_ => {},
diff --git a/src/tools/clippy/clippy_lints/src/format_args.rs b/src/tools/clippy/clippy_lints/src/format_args.rs
index 39abf5c2d..3c1f2d9d5 100644
--- a/src/tools/clippy/clippy_lints/src/format_args.rs
+++ b/src/tools/clippy/clippy_lints/src/format_args.rs
@@ -1,11 +1,11 @@
use arrayvec::ArrayVec;
+use clippy_config::msrvs::{self, Msrv};
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
use clippy_utils::is_diag_trait_item;
use clippy_utils::macros::{
find_format_arg_expr, find_format_args, format_arg_removal_span, format_placeholder_format_span, is_assert_macro,
is_format_macro, is_panic, root_macro_call, root_macro_call_first_node, FormatParamUsage,
};
-use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::source::snippet_opt;
use clippy_utils::ty::{implements_trait, is_type_lang_item};
use if_chain::if_chain;
@@ -35,12 +35,12 @@ declare_clippy_lint! {
/// The recommended code is both shorter and avoids a temporary allocation.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # use std::panic::Location;
/// println!("error: {}", format!("something failed at {}", Location::caller()));
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # use std::panic::Location;
/// println!("error: something failed at {}", Location::caller());
/// ```
@@ -61,12 +61,12 @@ declare_clippy_lint! {
/// unnecessary.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # use std::panic::Location;
/// println!("error: something failed at {}", Location::caller().to_string());
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # use std::panic::Location;
/// println!("error: something failed at {}", Location::caller());
/// ```
@@ -87,7 +87,7 @@ declare_clippy_lint! {
/// The inlined syntax, where allowed, is simpler.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # let var = 42;
/// # let width = 1;
/// # let prec = 2;
@@ -98,7 +98,7 @@ declare_clippy_lint! {
/// format!("{:.*}", prec, var);
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # let var = 42;
/// # let width = 1;
/// # let prec = 2;
@@ -111,12 +111,12 @@ declare_clippy_lint! {
///
/// If allow-mixed-uninlined-format-args is set to false in clippy.toml,
/// the following code will also trigger the lint:
- /// ```rust
+ /// ```no_run
/// # let var = 42;
/// format!("{} {}", var, 1+2);
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # let var = 42;
/// format!("{var} {}", 1+2);
/// ```
@@ -141,13 +141,13 @@ declare_clippy_lint! {
/// an expected formatting operation such as adding padding isn't happening.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// println!("{:.}", 1.0);
///
/// println!("not padded: {:5}", format_args!("..."));
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// println!("{}", 1.0);
///
/// println!("not padded: {}", format_args!("..."));
@@ -370,7 +370,7 @@ fn check_one_arg(
};
fixes.push((pos_span, replacement));
fixes.push((arg_span, String::new()));
- true // successful inlining, continue checking
+ true // successful inlining, continue checking
} else {
// Do not continue inlining (return false) in case
// * if we can't inline a numbered argument, e.g. `print!("{0} ...", foo.bar, ...)`
diff --git a/src/tools/clippy/clippy_lints/src/format_impl.rs b/src/tools/clippy/clippy_lints/src/format_impl.rs
index 1d2f7cb71..08ee7032c 100644
--- a/src/tools/clippy/clippy_lints/src/format_impl.rs
+++ b/src/tools/clippy/clippy_lints/src/format_impl.rs
@@ -21,7 +21,7 @@ declare_clippy_lint! {
///
/// ### Example
///
- /// ```rust
+ /// ```no_run
/// use std::fmt;
///
/// struct Structure(i32);
@@ -33,7 +33,7 @@ declare_clippy_lint! {
///
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// use std::fmt;
///
/// struct Structure(i32);
@@ -59,7 +59,7 @@ declare_clippy_lint! {
/// should write to the `Formatter`, not stdout/stderr.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// use std::fmt::{Display, Error, Formatter};
///
/// struct S;
@@ -72,7 +72,7 @@ declare_clippy_lint! {
/// }
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// use std::fmt::{Display, Error, Formatter};
///
/// struct S;
diff --git a/src/tools/clippy/clippy_lints/src/format_push_string.rs b/src/tools/clippy/clippy_lints/src/format_push_string.rs
index 45f67020c..ac45f5aed 100644
--- a/src/tools/clippy/clippy_lints/src/format_push_string.rs
+++ b/src/tools/clippy/clippy_lints/src/format_push_string.rs
@@ -21,13 +21,13 @@ declare_clippy_lint! {
/// While using `write!` in the suggested way should never fail, this isn't necessarily clear to the programmer.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let mut s = String::new();
/// s += &format!("0x{:X}", 1024);
/// s.push_str(&format!("0x{:X}", 1024));
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// use std::fmt::Write as _; // import without risk of name clashing
///
/// let mut s = String::new();
@@ -58,7 +58,7 @@ fn is_format(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
arms.iter().any(|arm| is_format(cx, arm.body))
},
Some(higher::IfLetOrMatch::IfLet(_, _, then, r#else)) => {
- is_format(cx, then) ||r#else.is_some_and(|e| is_format(cx, e))
+ is_format(cx, then) || r#else.is_some_and(|e| is_format(cx, e))
},
_ => false,
}
@@ -69,17 +69,15 @@ impl<'tcx> LateLintPass<'tcx> for FormatPushString {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
let arg = match expr.kind {
ExprKind::MethodCall(_, _, [arg], _) => {
- if let Some(fn_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) &&
- match_def_path(cx, fn_def_id, &paths::PUSH_STR) {
+ if let Some(fn_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id)
+ && match_def_path(cx, fn_def_id, &paths::PUSH_STR)
+ {
arg
} else {
return;
}
- }
- ExprKind::AssignOp(op, left, arg)
- if op.node == BinOpKind::Add && is_string(cx, left) => {
- arg
},
+ ExprKind::AssignOp(op, left, arg) if op.node == BinOpKind::Add && is_string(cx, left) => arg,
_ => return,
};
if is_format(cx, arg) {
diff --git a/src/tools/clippy/clippy_lints/src/formatting.rs b/src/tools/clippy/clippy_lints/src/formatting.rs
index d03480c21..2c9c43d3e 100644
--- a/src/tools/clippy/clippy_lints/src/formatting.rs
+++ b/src/tools/clippy/clippy_lints/src/formatting.rs
@@ -6,7 +6,7 @@ use rustc_ast::ast::{BinOpKind, Block, Expr, ExprKind, StmtKind, UnOp};
use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::source_map::Span;
+use rustc_span::Span;
declare_clippy_lint! {
/// ### What it does
@@ -37,7 +37,7 @@ declare_clippy_lint! {
/// This is either a typo in the binary operator or confusing.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # let foo = true;
/// # let bar = false;
/// // &&! looks like a different operator
@@ -45,7 +45,7 @@ declare_clippy_lint! {
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # let foo = true;
/// # let bar = false;
/// if foo && !bar {}
@@ -274,7 +274,7 @@ fn check_array(cx: &EarlyContext<'_>, expr: &Expr) {
for element in array {
if_chain! {
if let ExprKind::Binary(ref op, ref lhs, _) = element.kind;
- if has_unary_equivalent(op.node) && lhs.span.ctxt() == op.span.ctxt();
+ if has_unary_equivalent(op.node) && lhs.span.eq_ctxt(op.span);
let space_span = lhs.span.between(op.span);
if let Some(space_snippet) = snippet_opt(cx, space_span);
let lint_span = lhs.span.with_lo(lhs.span.hi());
diff --git a/src/tools/clippy/clippy_lints/src/four_forward_slashes.rs b/src/tools/clippy/clippy_lints/src/four_forward_slashes.rs
index 419c77343..69bc0b726 100644
--- a/src/tools/clippy/clippy_lints/src/four_forward_slashes.rs
+++ b/src/tools/clippy/clippy_lints/src/four_forward_slashes.rs
@@ -14,7 +14,7 @@ declare_clippy_lint! {
/// comment.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// //// My amazing data structure
/// pub struct Foo {
/// // ...
@@ -22,13 +22,13 @@ declare_clippy_lint! {
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// /// My amazing data structure
/// pub struct Foo {
/// // ...
/// }
/// ```
- #[clippy::version = "1.72.0"]
+ #[clippy::version = "1.73.0"]
pub FOUR_FORWARD_SLASHES,
suspicious,
"comments with 4 forward slashes (`////`) likely intended to be doc comments (`///`)"
diff --git a/src/tools/clippy/clippy_lints/src/from_over_into.rs b/src/tools/clippy/clippy_lints/src/from_over_into.rs
index 2b899e21e..5477532bb 100644
--- a/src/tools/clippy/clippy_lints/src/from_over_into.rs
+++ b/src/tools/clippy/clippy_lints/src/from_over_into.rs
@@ -1,6 +1,6 @@
+use clippy_config::msrvs::{self, Msrv};
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::macros::span_is_local;
-use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::path_def_id;
use clippy_utils::source::snippet_opt;
use rustc_errors::Applicability;
@@ -24,7 +24,7 @@ declare_clippy_lint! {
/// According the std docs implementing `From<..>` is preferred since it gives you `Into<..>` for free where the reverse isn't true.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// struct StringWrapper(String);
///
/// impl Into<StringWrapper> for String {
@@ -34,7 +34,7 @@ declare_clippy_lint! {
/// }
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// struct StringWrapper(String);
///
/// impl From<String> for StringWrapper {
@@ -88,7 +88,8 @@ impl<'tcx> LateLintPass<'tcx> for FromOverInto {
cx.tcx.sess.source_map().guess_head_span(item.span),
"an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true",
|diag| {
- // If the target type is likely foreign mention the orphan rules as it's a common source of confusion
+ // If the target type is likely foreign mention the orphan rules as it's a common source of
+ // confusion
if path_def_id(cx, target_ty.peel_refs()).map_or(true, |id| !id.is_local()) {
diag.help(
"`impl From<Local> for Foreign` is allowed by the orphan rules, for more information see\n\
@@ -96,7 +97,10 @@ impl<'tcx> LateLintPass<'tcx> for FromOverInto {
);
}
- let message = format!("replace the `Into` implementation with `From<{}>`", middle_trait_ref.self_ty());
+ let message = format!(
+ "replace the `Into` implementation with `From<{}>`",
+ middle_trait_ref.self_ty()
+ );
if let Some(suggestions) = convert_to_from(cx, into_trait_seg, target_ty, self_ty, impl_item_ref) {
diag.multipart_suggestion(message, suggestions, Applicability::MachineApplicable);
} else {
@@ -110,12 +114,12 @@ impl<'tcx> LateLintPass<'tcx> for FromOverInto {
extract_msrv_attr!(LateContext);
}
-/// Finds the occurences of `Self` and `self`
+/// Finds the occurrences of `Self` and `self`
struct SelfFinder<'a, 'tcx> {
cx: &'a LateContext<'tcx>,
- /// Occurences of `Self`
+ /// Occurrences of `Self`
upper: Vec<Span>,
- /// Occurences of `self`
+ /// Occurrences of `self`
lower: Vec<Span>,
/// If any of the `self`/`Self` usages were from an expansion, or the body contained a binding
/// already named `val`
diff --git a/src/tools/clippy/clippy_lints/src/from_raw_with_void_ptr.rs b/src/tools/clippy/clippy_lints/src/from_raw_with_void_ptr.rs
index 5e859d97c..d9138d48b 100644
--- a/src/tools/clippy/clippy_lints/src/from_raw_with_void_ptr.rs
+++ b/src/tools/clippy/clippy_lints/src/from_raw_with_void_ptr.rs
@@ -1,6 +1,6 @@
use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::path_def_id;
use clippy_utils::ty::is_c_void;
-use clippy_utils::{match_def_path, path_def_id, paths};
use rustc_hir::def_id::DefId;
use rustc_hir::{Expr, ExprKind, QPath};
use rustc_lint::{LateContext, LateLintPass};
@@ -18,13 +18,13 @@ declare_clippy_lint! {
/// For this to be safe, `c_void` would need to have the same memory layout as the original type, which is often not the case.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # use std::ffi::c_void;
/// let ptr = Box::into_raw(Box::new(42usize)) as *mut c_void;
/// let _ = unsafe { Box::from_raw(ptr) };
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # use std::ffi::c_void;
/// # let ptr = Box::into_raw(Box::new(42usize)) as *mut c_void;
/// let _ = unsafe { Box::from_raw(ptr as *mut usize) };
@@ -40,14 +40,22 @@ declare_lint_pass!(FromRawWithVoidPtr => [FROM_RAW_WITH_VOID_PTR]);
impl LateLintPass<'_> for FromRawWithVoidPtr {
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
if let ExprKind::Call(box_from_raw, [arg]) = expr.kind
- && let ExprKind::Path(QPath::TypeRelative(ty, seg)) = box_from_raw.kind
- && seg.ident.name == sym!(from_raw)
- && let Some(type_str) = path_def_id(cx, ty).and_then(|id| def_id_matches_type(cx, id))
- && let arg_kind = cx.typeck_results().expr_ty(arg).kind()
- && let RawPtr(TypeAndMut { ty, .. }) = arg_kind
- && is_c_void(cx, *ty) {
+ && let ExprKind::Path(QPath::TypeRelative(ty, seg)) = box_from_raw.kind
+ && seg.ident.name == sym!(from_raw)
+ && let Some(type_str) = path_def_id(cx, ty).and_then(|id| def_id_matches_type(cx, id))
+ && let arg_kind = cx.typeck_results().expr_ty(arg).kind()
+ && let RawPtr(TypeAndMut { ty, .. }) = arg_kind
+ && is_c_void(cx, *ty)
+ {
let msg = format!("creating a `{type_str}` from a void raw pointer");
- span_lint_and_help(cx, FROM_RAW_WITH_VOID_PTR, expr.span, &msg, Some(arg.span), "cast this to a pointer of the appropriate type");
+ span_lint_and_help(
+ cx,
+ FROM_RAW_WITH_VOID_PTR,
+ expr.span,
+ &msg,
+ Some(arg.span),
+ "cast this to a pointer of the appropriate type",
+ );
}
}
}
@@ -68,7 +76,7 @@ fn def_id_matches_type(cx: &LateContext<'_>, def_id: DefId) -> Option<&'static s
}
}
- if match_def_path(cx, def_id, &paths::WEAK_RC) || match_def_path(cx, def_id, &paths::WEAK_ARC) {
+ if matches!(cx.tcx.get_diagnostic_name(def_id), Some(sym::RcWeak | sym::ArcWeak)) {
Some("Weak")
} else {
None
diff --git a/src/tools/clippy/clippy_lints/src/functions/impl_trait_in_params.rs b/src/tools/clippy/clippy_lints/src/functions/impl_trait_in_params.rs
index 597fca888..ee66c841e 100644
--- a/src/tools/clippy/clippy_lints/src/functions/impl_trait_in_params.rs
+++ b/src/tools/clippy/clippy_lints/src/functions/impl_trait_in_params.rs
@@ -1,50 +1,104 @@
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::is_in_test_function;
+use rustc_hir as hir;
use rustc_hir::intravisit::FnKind;
-use rustc_hir::{Body, HirId};
+use rustc_hir::{Body, GenericParam, Generics, HirId, ImplItem, ImplItemKind, TraitItem, TraitItemKind};
use rustc_lint::LateContext;
-use rustc_span::Span;
+use rustc_span::symbol::Ident;
+use rustc_span::{BytePos, Span};
use super::IMPL_TRAIT_IN_PARAMS;
+fn report(
+ cx: &LateContext<'_>,
+ param: &GenericParam<'_>,
+ ident: &Ident,
+ generics: &Generics<'_>,
+ first_param_span: Span,
+) {
+ // No generics with nested generics, and no generics like FnMut(x)
+ span_lint_and_then(
+ cx,
+ IMPL_TRAIT_IN_PARAMS,
+ param.span,
+ "`impl Trait` used as a function parameter",
+ |diag| {
+ if let Some(gen_span) = generics.span_for_param_suggestion() {
+ // If there's already a generic param with the same bound, do not lint **this** suggestion.
+ diag.span_suggestion_with_style(
+ gen_span,
+ "add a type parameter",
+ format!(", {{ /* Generic name */ }}: {}", &param.name.ident().as_str()[5..]),
+ rustc_errors::Applicability::HasPlaceholders,
+ rustc_errors::SuggestionStyle::ShowAlways,
+ );
+ } else {
+ diag.span_suggestion_with_style(
+ Span::new(
+ first_param_span.lo() - rustc_span::BytePos(1),
+ ident.span.hi(),
+ ident.span.ctxt(),
+ ident.span.parent(),
+ ),
+ "add a type parameter",
+ format!("<{{ /* Generic name */ }}: {}>", &param.name.ident().as_str()[5..]),
+ rustc_errors::Applicability::HasPlaceholders,
+ rustc_errors::SuggestionStyle::ShowAlways,
+ );
+ }
+ },
+ );
+}
+
pub(super) fn check_fn<'tcx>(cx: &LateContext<'_>, kind: &'tcx FnKind<'_>, body: &'tcx Body<'_>, hir_id: HirId) {
- if cx.tcx.visibility(cx.tcx.hir().body_owner_def_id(body.id())).is_public() && !is_in_test_function(cx.tcx, hir_id)
- {
- if let FnKind::ItemFn(ident, generics, _) = kind {
+ if_chain! {
+ if let FnKind::ItemFn(ident, generics, _) = kind;
+ if cx.tcx.visibility(cx.tcx.hir().body_owner_def_id(body.id())).is_public();
+ if !is_in_test_function(cx.tcx, hir_id);
+ then {
for param in generics.params {
if param.is_impl_trait() {
- // No generics with nested generics, and no generics like FnMut(x)
- span_lint_and_then(
- cx,
- IMPL_TRAIT_IN_PARAMS,
- param.span,
- "'`impl Trait` used as a function parameter'",
- |diag| {
- if let Some(gen_span) = generics.span_for_param_suggestion() {
- diag.span_suggestion_with_style(
- gen_span,
- "add a type parameter",
- format!(", {{ /* Generic name */ }}: {}", &param.name.ident().as_str()[5..]),
- rustc_errors::Applicability::HasPlaceholders,
- rustc_errors::SuggestionStyle::ShowAlways,
- );
- } else {
- diag.span_suggestion_with_style(
- Span::new(
- body.params[0].span.lo() - rustc_span::BytePos(1),
- ident.span.hi(),
- ident.span.ctxt(),
- ident.span.parent(),
- ),
- "add a type parameter",
- format!("<{{ /* Generic name */ }}: {}>", &param.name.ident().as_str()[5..]),
- rustc_errors::Applicability::HasPlaceholders,
- rustc_errors::SuggestionStyle::ShowAlways,
- );
- }
- },
- );
+ report(cx, param, ident, generics, body.params[0].span);
+ };
+ }
+ }
+ }
+}
+
+pub(super) fn check_impl_item(cx: &LateContext<'_>, impl_item: &ImplItem<'_>) {
+ if_chain! {
+ if let ImplItemKind::Fn(_, body_id) = impl_item.kind;
+ if let hir::Node::Item(item) = cx.tcx.hir().get_parent(impl_item.hir_id());
+ if let hir::ItemKind::Impl(impl_) = item.kind;
+ if let hir::Impl { of_trait, .. } = *impl_;
+ if of_trait.is_none();
+ let body = cx.tcx.hir().body(body_id);
+ if cx.tcx.visibility(cx.tcx.hir().body_owner_def_id(body.id())).is_public();
+ if !is_in_test_function(cx.tcx, impl_item.hir_id());
+ then {
+ for param in impl_item.generics.params {
+ if param.is_impl_trait() {
+ report(cx, param, &impl_item.ident, impl_item.generics, body.params[0].span);
+ }
+ }
+ }
+ }
+}
+
+pub(super) fn check_trait_item(cx: &LateContext<'_>, trait_item: &TraitItem<'_>, avoid_breaking_exported_api: bool) {
+ if_chain! {
+ if !avoid_breaking_exported_api;
+ if let TraitItemKind::Fn(_, _) = trait_item.kind;
+ if let hir::Node::Item(item) = cx.tcx.hir().get_parent(trait_item.hir_id());
+ // ^^ (Will always be a trait)
+ if !item.vis_span.is_empty(); // Is public
+ if !is_in_test_function(cx.tcx, trait_item.hir_id());
+ then {
+ for param in trait_item.generics.params {
+ if param.is_impl_trait() {
+ let sp = trait_item.ident.span.with_hi(trait_item.ident.span.hi() + BytePos(1));
+ report(cx, param, &trait_item.ident, trait_item.generics, sp.shrink_to_hi());
}
}
}
diff --git a/src/tools/clippy/clippy_lints/src/functions/mod.rs b/src/tools/clippy/clippy_lints/src/functions/mod.rs
index ee10334c6..3f5cceec7 100644
--- a/src/tools/clippy/clippy_lints/src/functions/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/functions/mod.rs
@@ -23,7 +23,7 @@ declare_clippy_lint! {
/// grouping some parameters into a new type.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # struct Color;
/// fn foo(x: u32, y: u32, name: &str, c: Color, w: f32, h: f32, a: f32, b: f32) {
/// // ..
@@ -46,7 +46,7 @@ declare_clippy_lint! {
/// multiple functions.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// fn im_too_long() {
/// println!("");
/// // ... 100 more LoC
@@ -129,7 +129,7 @@ declare_clippy_lint! {
/// a remnant of a refactoring that removed the return type.
///
/// ### Examples
- /// ```rust
+ /// ```no_run
/// #[must_use]
/// fn useless() { }
/// ```
@@ -151,7 +151,7 @@ declare_clippy_lint! {
/// attribute to improve the lint message.
///
/// ### Examples
- /// ```rust
+ /// ```no_run
/// #[must_use]
/// fn double_must_use() -> Result<(), ()> {
/// unimplemented!();
@@ -183,7 +183,7 @@ declare_clippy_lint! {
/// `#[must_use]`.
///
/// ### Examples
- /// ```rust
+ /// ```no_run
/// // this could be annotated with `#[must_use]`.
/// pub fn id<T>(t: T) -> T { t }
/// ```
@@ -211,7 +211,7 @@ declare_clippy_lint! {
/// instead.
///
/// ### Examples
- /// ```rust
+ /// ```no_run
/// pub fn read_u8() -> Result<u8, ()> { Err(()) }
/// ```
/// should become
@@ -262,7 +262,7 @@ declare_clippy_lint! {
/// The size determined by Clippy is platform-dependent.
///
/// ### Examples
- /// ```rust
+ /// ```no_run
/// pub enum ParseError {
/// UnparsedBytes([u8; 512]),
/// UnexpectedEof,
@@ -274,7 +274,7 @@ declare_clippy_lint! {
/// }
/// ```
/// should be
- /// ```
+ /// ```no_run
/// pub enum ParseError {
/// UnparsedBytes(Box<[u8; 512]>),
/// UnexpectedEof,
@@ -301,7 +301,7 @@ declare_clippy_lint! {
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// struct A {
/// a: String,
/// b: String,
@@ -315,7 +315,7 @@ declare_clippy_lint! {
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// struct A {
/// a: String,
/// b: String,
@@ -340,14 +340,14 @@ declare_clippy_lint! {
/// Turbofish syntax (`::<>`) cannot be used when `impl Trait` is being used, making `impl Trait` less powerful. Readability may also be a factor.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// trait MyTrait {}
/// fn foo(a: impl MyTrait) {
/// // [...]
/// }
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// trait MyTrait {}
/// fn foo<T: MyTrait>(a: T) {
/// // [...]
@@ -360,18 +360,26 @@ declare_clippy_lint! {
}
#[derive(Copy, Clone)]
+#[allow(clippy::struct_field_names)]
pub struct Functions {
too_many_arguments_threshold: u64,
too_many_lines_threshold: u64,
large_error_threshold: u64,
+ avoid_breaking_exported_api: bool,
}
impl Functions {
- pub fn new(too_many_arguments_threshold: u64, too_many_lines_threshold: u64, large_error_threshold: u64) -> Self {
+ pub fn new(
+ too_many_arguments_threshold: u64,
+ too_many_lines_threshold: u64,
+ large_error_threshold: u64,
+ avoid_breaking_exported_api: bool,
+ ) -> Self {
Self {
too_many_arguments_threshold,
too_many_lines_threshold,
large_error_threshold,
+ avoid_breaking_exported_api,
}
}
}
@@ -415,6 +423,7 @@ impl<'tcx> LateLintPass<'tcx> for Functions {
fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'_>) {
must_use::check_impl_item(cx, item);
result::check_impl_item(cx, item, self.large_error_threshold);
+ impl_trait_in_params::check_impl_item(cx, item);
}
fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) {
@@ -422,5 +431,6 @@ impl<'tcx> LateLintPass<'tcx> for Functions {
not_unsafe_ptr_arg_deref::check_trait_item(cx, item);
must_use::check_trait_item(cx, item);
result::check_trait_item(cx, item, self.large_error_threshold);
+ impl_trait_in_params::check_trait_item(cx, item, self.avoid_breaking_exported_api);
}
}
diff --git a/src/tools/clippy/clippy_lints/src/functions/must_use.rs b/src/tools/clippy/clippy_lints/src/functions/must_use.rs
index 57df5683c..3aaf63ce3 100644
--- a/src/tools/clippy/clippy_lints/src/functions/must_use.rs
+++ b/src/tools/clippy/clippy_lints/src/functions/must_use.rs
@@ -118,9 +118,10 @@ fn check_needless_must_use(
if sig.header.is_async() {
let infcx = cx.tcx.infer_ctxt().build();
if let Some(future_ty) = infcx.get_impl_future_output_ty(return_ty(cx, item_id))
- && !is_must_use_ty(cx, future_ty) {
- return;
- }
+ && !is_must_use_ty(cx, future_ty)
+ {
+ return;
+ }
}
span_lint_and_help(
diff --git a/src/tools/clippy/clippy_lints/src/functions/result.rs b/src/tools/clippy/clippy_lints/src/functions/result.rs
index 90fc0d4f6..485235514 100644
--- a/src/tools/clippy/clippy_lints/src/functions/result.rs
+++ b/src/tools/clippy/clippy_lints/src/functions/result.rs
@@ -21,7 +21,9 @@ fn result_err_ty<'tcx>(
) -> Option<(&'tcx hir::Ty<'tcx>, Ty<'tcx>)> {
if !in_external_macro(cx.sess(), item_span)
&& let hir::FnRetTy::Return(hir_ty) = decl.output
- && let ty = cx.tcx.erase_late_bound_regions(cx.tcx.fn_sig(id).instantiate_identity().output())
+ && let ty = cx
+ .tcx
+ .erase_late_bound_regions(cx.tcx.fn_sig(id).instantiate_identity().output())
&& is_type_diagnostic_item(cx, ty, sym::Result)
&& let ty::Adt(_, args) = ty.kind()
{
diff --git a/src/tools/clippy/clippy_lints/src/future_not_send.rs b/src/tools/clippy/clippy_lints/src/future_not_send.rs
index 621415c88..eee5b7540 100644
--- a/src/tools/clippy/clippy_lints/src/future_not_send.rs
+++ b/src/tools/clippy/clippy_lints/src/future_not_send.rs
@@ -34,11 +34,11 @@ declare_clippy_lint! {
/// produced.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// async fn not_send(bytes: std::rc::Rc<[u8]>) {}
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// async fn is_send(bytes: std::sync::Arc<[u8]>) {}
/// ```
#[clippy::version = "1.44.0"]
diff --git a/src/tools/clippy/clippy_lints/src/if_not_else.rs b/src/tools/clippy/clippy_lints/src/if_not_else.rs
index 3d59b7833..cae561f78 100644
--- a/src/tools/clippy/clippy_lints/src/if_not_else.rs
+++ b/src/tools/clippy/clippy_lints/src/if_not_else.rs
@@ -1,6 +1,7 @@
//! lint on if branches that could be swapped so no `!` operation is necessary
//! on the condition
+use clippy_utils::consts::{constant_simple, Constant};
use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::is_else_clause;
use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp};
@@ -16,7 +17,7 @@ declare_clippy_lint! {
/// Negations reduce the readability of statements.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # let v: Vec<usize> = vec![];
/// # fn a() {}
/// # fn b() {}
@@ -29,7 +30,7 @@ declare_clippy_lint! {
///
/// Could be written:
///
- /// ```rust
+ /// ```no_run
/// # let v: Vec<usize> = vec![];
/// # fn a() {}
/// # fn b() {}
@@ -47,6 +48,13 @@ declare_clippy_lint! {
declare_lint_pass!(IfNotElse => [IF_NOT_ELSE]);
+fn is_zero_const(expr: &Expr<'_>, cx: &LateContext<'_>) -> bool {
+ if let Some(value) = constant_simple(cx, cx.typeck_results(), expr) {
+ return Constant::Int(0) == value;
+ }
+ false
+}
+
impl LateLintPass<'_> for IfNotElse {
fn check_expr(&mut self, cx: &LateContext<'_>, item: &Expr<'_>) {
// While loops will be desugared to ExprKind::If. This will cause the lint to fire.
@@ -72,7 +80,9 @@ impl LateLintPass<'_> for IfNotElse {
"remove the `!` and swap the blocks of the `if`/`else`",
);
},
- ExprKind::Binary(ref kind, _, _) if kind.node == BinOpKind::Ne => {
+ ExprKind::Binary(ref kind, _, lhs) if kind.node == BinOpKind::Ne && !is_zero_const(lhs, cx) => {
+ // Disable firing the lint on `… != 0`, as these are likely to be bit tests.
+ // For example, `if foo & 0x0F00 != 0 { … } else { … }` already is in the "proper" order.
span_lint_and_help(
cx,
IF_NOT_ELSE,
diff --git a/src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs b/src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs
index e2d19e245..66c10ab22 100644
--- a/src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs
+++ b/src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs
@@ -1,6 +1,6 @@
+use clippy_config::msrvs::{self, Msrv};
use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::eager_or_lazy::switch_to_eager_eval;
-use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::source::snippet_with_context;
use clippy_utils::sugg::Sugg;
use clippy_utils::{contains_return, higher, is_else_clause, is_res_lang_ctor, path_res, peel_blocks};
@@ -21,7 +21,7 @@ declare_clippy_lint! {
/// in comparison to `bool::then`.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # let v = vec![0];
/// let a = if v.is_empty() {
/// println!("true!");
@@ -33,7 +33,7 @@ declare_clippy_lint! {
///
/// Could be written:
///
- /// ```rust
+ /// ```no_run
/// # let v = vec![0];
/// let a = v.is_empty().then(|| {
/// println!("true!");
@@ -76,7 +76,11 @@ impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone {
let ctxt = expr.span.ctxt();
- if let Some(higher::If { cond, then, r#else: Some(els) }) = higher::If::hir(expr)
+ if let Some(higher::If {
+ cond,
+ then,
+ r#else: Some(els),
+ }) = higher::If::hir(expr)
&& let ExprKind::Block(then_block, _) = then.kind
&& let Some(then_expr) = then_block.expr
&& let ExprKind::Call(then_call, [then_arg]) = then_expr.kind
@@ -86,7 +90,9 @@ impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone {
&& !contains_return(then_block.stmts)
{
let mut app = Applicability::Unspecified;
- let cond_snip = Sugg::hir_with_context(cx, cond, expr.span.ctxt(), "[condition]", &mut app).maybe_par().to_string();
+ let cond_snip = Sugg::hir_with_context(cx, cond, expr.span.ctxt(), "[condition]", &mut app)
+ .maybe_par()
+ .to_string();
let arg_snip = snippet_with_context(cx, then_arg.span, ctxt, "[body]", &mut app).0;
let mut method_body = if then_block.stmts.is_empty() {
arg_snip.into_owned()
@@ -100,9 +106,8 @@ impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone {
"then"
};
- let help = format!(
- "consider using `bool::{method_name}` like: `{cond_snip}.{method_name}({method_body})`",
- );
+ let help =
+ format!("consider using `bool::{method_name}` like: `{cond_snip}.{method_name}({method_body})`",);
span_lint_and_help(
cx,
IF_THEN_SOME_ELSE_NONE,
diff --git a/src/tools/clippy/clippy_lints/src/ignored_unit_patterns.rs b/src/tools/clippy/clippy_lints/src/ignored_unit_patterns.rs
index d8ead1c9d..76bdfb94e 100644
--- a/src/tools/clippy/clippy_lints/src/ignored_unit_patterns.rs
+++ b/src/tools/clippy/clippy_lints/src/ignored_unit_patterns.rs
@@ -15,14 +15,14 @@ declare_clippy_lint! {
/// would detect a type change that `_` would ignore.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// match std::fs::create_dir("tmp-work-dir") {
/// Ok(_) => println!("Working directory created"),
/// Err(s) => eprintln!("Could not create directory: {s}"),
/// }
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// match std::fs::create_dir("tmp-work-dir") {
/// Ok(()) => println!("Working directory created"),
/// Err(s) => eprintln!("Could not create directory: {s}"),
@@ -37,6 +37,10 @@ declare_lint_pass!(IgnoredUnitPatterns => [IGNORED_UNIT_PATTERNS]);
impl<'tcx> LateLintPass<'tcx> for IgnoredUnitPatterns {
fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx hir::Pat<'tcx>) {
+ if pat.span.from_expansion() {
+ return;
+ }
+
match cx.tcx.hir().get_parent(pat.hir_id) {
Node::Param(param) if matches!(cx.tcx.hir().get_parent(param.hir_id), Node::Item(_)) => {
// Ignore function parameters
@@ -48,7 +52,7 @@ impl<'tcx> LateLintPass<'tcx> for IgnoredUnitPatterns {
},
_ => {},
}
- if matches!(pat.kind, PatKind::Wild) && cx.typeck_results().pat_ty(pat).is_unit() {
+ if matches!(pat.kind, PatKind::Wild) && cx.typeck_results().pat_ty(pat).peel_refs().is_unit() {
span_lint_and_sugg(
cx,
IGNORED_UNIT_PATTERNS,
diff --git a/src/tools/clippy/clippy_lints/src/implicit_hasher.rs b/src/tools/clippy/clippy_lints/src/implicit_hasher.rs
index 64a4a3fa7..eaf80de38 100644
--- a/src/tools/clippy/clippy_lints/src/implicit_hasher.rs
+++ b/src/tools/clippy/clippy_lints/src/implicit_hasher.rs
@@ -6,12 +6,11 @@ use rustc_hir as hir;
use rustc_hir::intravisit::{walk_body, walk_expr, walk_inf, walk_ty, Visitor};
use rustc_hir::{Body, Expr, ExprKind, GenericArg, Item, ItemKind, QPath, TyKind};
use rustc_hir_analysis::hir_ty_to_ty;
-use rustc_lint::{LateContext, LateLintPass, LintContext};
+use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::hir::nested_filter;
-use rustc_middle::lint::in_external_macro;
use rustc_middle::ty::{Ty, TypeckResults};
use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::source_map::Span;
+use rustc_span::Span;
use rustc_span::symbol::sym;
use if_chain::if_chain;
@@ -36,7 +35,7 @@ declare_clippy_lint! {
/// pieces of code, possibly including external crates.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # use std::collections::HashMap;
/// # use std::hash::{Hash, BuildHasher};
/// # trait Serialize {};
@@ -45,7 +44,7 @@ declare_clippy_lint! {
/// pub fn foo(map: &mut HashMap<i32, i32>) { }
/// ```
/// could be rewritten as
- /// ```rust
+ /// ```no_run
/// # use std::collections::HashMap;
/// # use std::hash::{Hash, BuildHasher};
/// # trait Serialize {};
@@ -162,7 +161,7 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitHasher {
vis.visit_ty(ty);
for target in &vis.found {
- if in_external_macro(cx.sess(), generics.span) {
+ if generics.span.from_expansion() {
continue;
}
let generics_suggestion_span = generics.span.substitute_dummy({
diff --git a/src/tools/clippy/clippy_lints/src/implicit_return.rs b/src/tools/clippy/clippy_lints/src/implicit_return.rs
index a6b035d51..c6bcf3ba4 100644
--- a/src/tools/clippy/clippy_lints/src/implicit_return.rs
+++ b/src/tools/clippy/clippy_lints/src/implicit_return.rs
@@ -24,13 +24,13 @@ declare_clippy_lint! {
/// corresponding statements.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// fn foo(x: usize) -> usize {
/// x
/// }
/// ```
/// add return
- /// ```rust
+ /// ```no_run
/// fn foo(x: usize) -> usize {
/// return x;
/// }
diff --git a/src/tools/clippy/clippy_lints/src/implicit_saturating_add.rs b/src/tools/clippy/clippy_lints/src/implicit_saturating_add.rs
index ee7973b82..24f62490f 100644
--- a/src/tools/clippy/clippy_lints/src/implicit_saturating_add.rs
+++ b/src/tools/clippy/clippy_lints/src/implicit_saturating_add.rs
@@ -18,7 +18,7 @@ declare_clippy_lint! {
/// The built-in function is more readable and may be faster.
///
/// ### Example
- /// ```rust
+ /// ```no_run
///let mut u:u32 = 7000;
///
/// if u != u32::MAX {
@@ -26,7 +26,7 @@ declare_clippy_lint! {
/// }
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
///let mut u:u32 = 7000;
///
/// u = u.saturating_add(1);
@@ -82,18 +82,18 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingAdd {
fn get_int_max(ty: Ty<'_>) -> Option<u128> {
match ty.peel_refs().kind() {
- Int(IntTy::I8) => i8::max_value().try_into().ok(),
- Int(IntTy::I16) => i16::max_value().try_into().ok(),
- Int(IntTy::I32) => i32::max_value().try_into().ok(),
- Int(IntTy::I64) => i64::max_value().try_into().ok(),
- Int(IntTy::I128) => i128::max_value().try_into().ok(),
- Int(IntTy::Isize) => isize::max_value().try_into().ok(),
- Uint(UintTy::U8) => u8::max_value().try_into().ok(),
- Uint(UintTy::U16) => u16::max_value().try_into().ok(),
- Uint(UintTy::U32) => u32::max_value().try_into().ok(),
- Uint(UintTy::U64) => u64::max_value().try_into().ok(),
- Uint(UintTy::U128) => Some(u128::max_value()),
- Uint(UintTy::Usize) => usize::max_value().try_into().ok(),
+ Int(IntTy::I8) => i8::MAX.try_into().ok(),
+ Int(IntTy::I16) => i16::MAX.try_into().ok(),
+ Int(IntTy::I32) => i32::MAX.try_into().ok(),
+ Int(IntTy::I64) => i64::MAX.try_into().ok(),
+ Int(IntTy::I128) => i128::MAX.try_into().ok(),
+ Int(IntTy::Isize) => isize::MAX.try_into().ok(),
+ Uint(UintTy::U8) => Some(u8::MAX.into()),
+ Uint(UintTy::U16) => Some(u16::MAX.into()),
+ Uint(UintTy::U32) => Some(u32::MAX.into()),
+ Uint(UintTy::U64) => Some(u64::MAX.into()),
+ Uint(UintTy::U128) => Some(u128::MAX),
+ Uint(UintTy::Usize) => usize::MAX.try_into().ok(),
_ => None,
}
}
diff --git a/src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs b/src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs
index b99d45446..859404289 100644
--- a/src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs
+++ b/src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs
@@ -15,7 +15,7 @@ declare_clippy_lint! {
/// Simplicity and readability. Instead we can easily use an builtin function.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # let end: u32 = 10;
/// # let start: u32 = 5;
/// let mut i: u32 = end - start;
@@ -26,7 +26,7 @@ declare_clippy_lint! {
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # let end: u32 = 10;
/// # let start: u32 = 5;
/// let mut i: u32 = end - start;
diff --git a/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs b/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs
index ec9044bba..ff27a5d66 100644
--- a/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs
+++ b/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs
@@ -29,7 +29,7 @@ declare_clippy_lint! {
/// (e.g. `trait A {} trait B: A {} trait C: B {}`, then having an `fn() -> impl A + C`)
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # use std::ops::{Deref,DerefMut};
/// fn f() -> impl Deref<Target = i32> + DerefMut<Target = i32> {
/// // ^^^^^^^^^^^^^^^^^^^ unnecessary bound, already implied by the `DerefMut` trait bound
@@ -37,7 +37,7 @@ declare_clippy_lint! {
/// }
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # use std::ops::{Deref,DerefMut};
/// fn f() -> impl DerefMut<Target = i32> {
/// Box::new(123)
@@ -230,19 +230,24 @@ fn check(cx: &LateContext<'_>, decl: &FnDecl<'_>) {
// Example:
// `impl Deref<Target = i32> + DerefMut<Target = u32>` is not allowed.
// `DerefMut::Target` needs to match `Deref::Target`.
- let implied_bounds: Vec<_> = opaque_ty.bounds.iter().filter_map(|bound| {
- if let GenericBound::Trait(poly_trait, TraitBoundModifier::None) = bound
- && let [.., path] = poly_trait.trait_ref.path.segments
- && poly_trait.bound_generic_params.is_empty()
- && let Some(trait_def_id) = path.res.opt_def_id()
- && let predicates = cx.tcx.super_predicates_of(trait_def_id).predicates
- && !predicates.is_empty() // If the trait has no supertrait, there is nothing to add.
- {
- Some((bound.span(), path, predicates, trait_def_id))
- } else {
- None
- }
- }).collect();
+ let implied_bounds: Vec<_> = opaque_ty
+ .bounds
+ .iter()
+ .filter_map(|bound| {
+ if let GenericBound::Trait(poly_trait, TraitBoundModifier::None) = bound
+ && let [.., path] = poly_trait.trait_ref.path.segments
+ && poly_trait.bound_generic_params.is_empty()
+ && let Some(trait_def_id) = path.res.opt_def_id()
+ && let predicates = cx.tcx.super_predicates_of(trait_def_id).predicates
+ && !predicates.is_empty()
+ // If the trait has no supertrait, there is nothing to add.
+ {
+ Some((bound.span(), path, predicates, trait_def_id))
+ } else {
+ None
+ }
+ })
+ .collect();
// Lint all bounds in the `impl Trait` type that are also in the `implied_bounds` vec.
// This involves some extra logic when generic arguments are present, since
@@ -253,30 +258,31 @@ fn check(cx: &LateContext<'_>, decl: &FnDecl<'_>) {
&& let implied_args = path.args.map_or([].as_slice(), |a| a.args)
&& let implied_bindings = path.args.map_or([].as_slice(), |a| a.bindings)
&& let Some(def_id) = poly_trait.trait_ref.path.res.opt_def_id()
- && let Some((implied_by_span, implied_by_args, implied_by_bindings)) = implied_bounds
- .iter()
- .find_map(|&(span, implied_by_path, preds, implied_by_def_id)| {
- let implied_by_args = implied_by_path.args.map_or([].as_slice(), |a| a.args);
- let implied_by_bindings = implied_by_path.args.map_or([].as_slice(), |a| a.bindings);
+ && let Some((implied_by_span, implied_by_args, implied_by_bindings)) =
+ implied_bounds
+ .iter()
+ .find_map(|&(span, implied_by_path, preds, implied_by_def_id)| {
+ let implied_by_args = implied_by_path.args.map_or([].as_slice(), |a| a.args);
+ let implied_by_bindings = implied_by_path.args.map_or([].as_slice(), |a| a.bindings);
- preds.iter().find_map(|(clause, _)| {
- if let ClauseKind::Trait(tr) = clause.kind().skip_binder()
- && tr.def_id() == def_id
- && is_same_generics(
- cx.tcx,
- tr.trait_ref.args,
- implied_by_args,
- implied_args,
- implied_by_def_id,
- def_id,
- )
- {
- Some((span, implied_by_args, implied_by_bindings))
- } else {
- None
- }
+ preds.iter().find_map(|(clause, _)| {
+ if let ClauseKind::Trait(tr) = clause.kind().skip_binder()
+ && tr.def_id() == def_id
+ && is_same_generics(
+ cx.tcx,
+ tr.trait_ref.args,
+ implied_by_args,
+ implied_args,
+ implied_by_def_id,
+ def_id,
+ )
+ {
+ Some((span, implied_by_args, implied_by_bindings))
+ } else {
+ None
+ }
+ })
})
- })
{
emit_lint(
cx,
@@ -286,7 +292,7 @@ fn check(cx: &LateContext<'_>, decl: &FnDecl<'_>) {
implied_bindings,
implied_by_bindings,
implied_by_args,
- implied_by_span
+ implied_by_span,
);
}
}
diff --git a/src/tools/clippy/clippy_lints/src/inconsistent_struct_constructor.rs b/src/tools/clippy/clippy_lints/src/inconsistent_struct_constructor.rs
index 1ad886f2c..a84f7351a 100644
--- a/src/tools/clippy/clippy_lints/src/inconsistent_struct_constructor.rs
+++ b/src/tools/clippy/clippy_lints/src/inconsistent_struct_constructor.rs
@@ -19,7 +19,7 @@ declare_clippy_lint! {
/// Since the order of fields in a constructor doesn't affect the
/// resulted instance as the below example indicates,
///
- /// ```rust
+ /// ```no_run
/// #[derive(Debug, PartialEq, Eq)]
/// struct Foo {
/// x: i32,
@@ -35,7 +35,7 @@ declare_clippy_lint! {
/// inconsistent order can be confusing and decreases readability and consistency.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// struct Foo {
/// x: i32,
/// y: i32,
@@ -47,7 +47,7 @@ declare_clippy_lint! {
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # struct Foo {
/// # x: i32,
/// # y: i32,
diff --git a/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs b/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs
index f507f45d5..c2f1f18e3 100644
--- a/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs
+++ b/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs
@@ -1,7 +1,7 @@
+use clippy_config::msrvs::{self, Msrv};
use clippy_utils::consts::{constant, Constant};
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::higher::IfLet;
-use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::ty::is_copy;
use clippy_utils::{is_expn_of, is_lint_allowed, path_to_local};
use if_chain::if_chain;
@@ -31,7 +31,7 @@ declare_clippy_lint! {
/// patterns.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let slice: Option<&[u32]> = Some(&[1, 2, 3]);
///
/// if let Some(slice) = slice {
@@ -39,7 +39,7 @@ declare_clippy_lint! {
/// }
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// let slice: Option<&[u32]> = Some(&[1, 2, 3]);
///
/// if let Some(&[first, ..]) = slice {
diff --git a/src/tools/clippy/clippy_lints/src/indexing_slicing.rs b/src/tools/clippy/clippy_lints/src/indexing_slicing.rs
index 4f4f57177..1ce7d85d3 100644
--- a/src/tools/clippy/clippy_lints/src/indexing_slicing.rs
+++ b/src/tools/clippy/clippy_lints/src/indexing_slicing.rs
@@ -26,7 +26,7 @@ declare_clippy_lint! {
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # let x = [1, 2, 3, 4];
/// // Index within bounds
///
@@ -65,7 +65,7 @@ declare_clippy_lint! {
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # #![allow(unused)]
///
/// # let x = vec![0; 5];
diff --git a/src/tools/clippy/clippy_lints/src/infinite_iter.rs b/src/tools/clippy/clippy_lints/src/infinite_iter.rs
index fe28c526b..e9c53671a 100644
--- a/src/tools/clippy/clippy_lints/src/infinite_iter.rs
+++ b/src/tools/clippy/clippy_lints/src/infinite_iter.rs
@@ -39,7 +39,7 @@ declare_clippy_lint! {
/// this lint is not clever enough to analyze it.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let infinite_iter = 0..;
/// # #[allow(unused)]
/// [0..].iter().zip(infinite_iter.take_while(|x| *x > 5));
diff --git a/src/tools/clippy/clippy_lints/src/inherent_impl.rs b/src/tools/clippy/clippy_lints/src/inherent_impl.rs
index 3d1113ff9..a61a64161 100644
--- a/src/tools/clippy/clippy_lints/src/inherent_impl.rs
+++ b/src/tools/clippy/clippy_lints/src/inherent_impl.rs
@@ -18,7 +18,7 @@ declare_clippy_lint! {
/// Splitting the implementation of a type makes the code harder to navigate.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// struct X;
/// impl X {
/// fn one() {}
@@ -30,7 +30,7 @@ declare_clippy_lint! {
///
/// Could be written:
///
- /// ```rust
+ /// ```no_run
/// struct X;
/// impl X {
/// fn one() {}
diff --git a/src/tools/clippy/clippy_lints/src/inherent_to_string.rs b/src/tools/clippy/clippy_lints/src/inherent_to_string.rs
index bc4ec33b7..fe5eb5cca 100644
--- a/src/tools/clippy/clippy_lints/src/inherent_to_string.rs
+++ b/src/tools/clippy/clippy_lints/src/inherent_to_string.rs
@@ -15,7 +15,7 @@ declare_clippy_lint! {
/// This method is also implicitly defined if a type implements the `Display` trait. As the functionality of `Display` is much more versatile, it should be preferred.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// pub struct A;
///
/// impl A {
@@ -26,7 +26,7 @@ declare_clippy_lint! {
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// use std::fmt;
///
/// pub struct A;
@@ -51,7 +51,7 @@ declare_clippy_lint! {
/// This method is also implicitly defined if a type implements the `Display` trait. The less versatile inherent method will then shadow the implementation introduced by `Display`.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// use std::fmt;
///
/// pub struct A;
@@ -70,7 +70,7 @@ declare_clippy_lint! {
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// use std::fmt;
///
/// pub struct A;
diff --git a/src/tools/clippy/clippy_lints/src/init_numbered_fields.rs b/src/tools/clippy/clippy_lints/src/init_numbered_fields.rs
index f95d2c2ed..269311a67 100644
--- a/src/tools/clippy/clippy_lints/src/init_numbered_fields.rs
+++ b/src/tools/clippy/clippy_lints/src/init_numbered_fields.rs
@@ -20,7 +20,7 @@ declare_clippy_lint! {
/// benefit as opposed to tuple initializers
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// struct TupleStruct(u8, u16);
///
/// let _ = TupleStruct {
diff --git a/src/tools/clippy/clippy_lints/src/inline_fn_without_body.rs b/src/tools/clippy/clippy_lints/src/inline_fn_without_body.rs
index d609a5ca4..899126565 100644
--- a/src/tools/clippy/clippy_lints/src/inline_fn_without_body.rs
+++ b/src/tools/clippy/clippy_lints/src/inline_fn_without_body.rs
@@ -18,7 +18,7 @@ declare_clippy_lint! {
/// The inline attribute is ignored for trait methods without bodies.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// trait Animal {
/// #[inline]
/// fn name(&self) -> &'static str;
diff --git a/src/tools/clippy/clippy_lints/src/instant_subtraction.rs b/src/tools/clippy/clippy_lints/src/instant_subtraction.rs
index 8df7dfb8b..32b2cb438 100644
--- a/src/tools/clippy/clippy_lints/src/instant_subtraction.rs
+++ b/src/tools/clippy/clippy_lints/src/instant_subtraction.rs
@@ -1,5 +1,5 @@
+use clippy_config::msrvs::{self, Msrv};
use clippy_utils::diagnostics::{self, span_lint_and_sugg};
-use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::source::snippet_with_context;
use clippy_utils::sugg::Sugg;
use clippy_utils::ty;
@@ -21,13 +21,13 @@ declare_clippy_lint! {
/// `prev_instant.elapsed()` also more clearly signals intention.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// use std::time::Instant;
/// let prev_instant = Instant::now();
/// let duration = Instant::now() - prev_instant;
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// use std::time::Instant;
/// let prev_instant = Instant::now();
/// let duration = prev_instant.elapsed();
@@ -47,13 +47,13 @@ declare_clippy_lint! {
/// unintentional panics.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # use std::time::{Instant, Duration};
/// let time_passed = Instant::now() - Duration::from_secs(5);
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # use std::time::{Instant, Duration};
/// let time_passed = Instant::now().checked_sub(Duration::from_secs(5));
/// ```
@@ -130,11 +130,7 @@ fn is_instant_now_call(cx: &LateContext<'_>, expr_block: &'_ Expr<'_>) -> bool {
fn is_an_instant(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
let expr_ty = cx.typeck_results().expr_ty(expr);
-
- match expr_ty.kind() {
- rustc_middle::ty::Adt(def, _) => clippy_utils::match_def_path(cx, def.did(), &clippy_utils::paths::INSTANT),
- _ => false,
- }
+ ty::is_type_diagnostic_item(cx, expr_ty, sym::Instant)
}
fn is_a_duration(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
diff --git a/src/tools/clippy/clippy_lints/src/int_plus_one.rs b/src/tools/clippy/clippy_lints/src/int_plus_one.rs
index 1b14e525d..9ffcee07d 100644
--- a/src/tools/clippy/clippy_lints/src/int_plus_one.rs
+++ b/src/tools/clippy/clippy_lints/src/int_plus_one.rs
@@ -16,14 +16,14 @@ declare_clippy_lint! {
/// Readability -- better to use `> y` instead of `>= y + 1`.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # let x = 1;
/// # let y = 1;
/// if x >= y + 1 {}
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # let x = 1;
/// # let y = 1;
/// if x > y {}
diff --git a/src/tools/clippy/clippy_lints/src/invalid_upcast_comparisons.rs b/src/tools/clippy/clippy_lints/src/invalid_upcast_comparisons.rs
index 6ea637412..de82935e6 100644
--- a/src/tools/clippy/clippy_lints/src/invalid_upcast_comparisons.rs
+++ b/src/tools/clippy/clippy_lints/src/invalid_upcast_comparisons.rs
@@ -26,7 +26,7 @@ declare_clippy_lint! {
/// https://github.com/rust-lang/rust-clippy/issues/886
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let x: u8 = 1;
/// (x as u32) > 300;
/// ```
diff --git a/src/tools/clippy/clippy_lints/src/enum_variants.rs b/src/tools/clippy/clippy_lints/src/item_name_repetitions.rs
index e332f681b..90048d96c 100644
--- a/src/tools/clippy/clippy_lints/src/enum_variants.rs
+++ b/src/tools/clippy/clippy_lints/src/item_name_repetitions.rs
@@ -1,12 +1,13 @@
//! lint on enum variants that are prefixed or suffixed by the same characters
use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_hir};
+use clippy_utils::macros::span_is_local;
use clippy_utils::source::is_present_in_source;
-use clippy_utils::str_utils::{camel_case_split, count_match_end, count_match_start};
-use rustc_hir::{EnumDef, Item, ItemKind, OwnerId, Variant};
+use clippy_utils::str_utils::{camel_case_split, count_match_end, count_match_start, to_camel_case, to_snake_case};
+use rustc_hir::{EnumDef, FieldDef, Item, ItemKind, OwnerId, Variant, VariantData};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_tool_lint, impl_lint_pass};
-use rustc_span::source_map::Span;
+use rustc_span::Span;
use rustc_span::symbol::Symbol;
declare_clippy_lint! {
@@ -25,7 +26,7 @@ declare_clippy_lint! {
/// (the prefixes are `Foo1` and `Foo2` respectively), as also `Bar螃`, `Bar蟹`
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// enum Cake {
/// BlackForestCake,
/// HummingbirdCake,
@@ -33,7 +34,7 @@ declare_clippy_lint! {
/// }
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// enum Cake {
/// BlackForest,
/// Hummingbird,
@@ -55,14 +56,14 @@ declare_clippy_lint! {
/// It requires the user to type the module name twice.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// mod cake {
/// struct BlackForestCake;
/// }
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// mod cake {
/// struct BlackForest;
/// }
@@ -103,32 +104,184 @@ declare_clippy_lint! {
style,
"modules that have the same name as their parent module"
}
+declare_clippy_lint! {
+ /// ### What it does
+ /// Detects struct fields that are prefixed or suffixed
+ /// by the same characters or the name of the struct itself.
+ ///
+ /// ### Why is this bad?
+ /// Information common to all struct fields is better represented in the struct name.
+ ///
+ /// ### Limitations
+ /// Characters with no casing will be considered when comparing prefixes/suffixes
+ /// This applies to numbers and non-ascii characters without casing
+ /// e.g. `foo1` and `foo2` is considered to have different prefixes
+ /// (the prefixes are `foo1` and `foo2` respectively), as also `bar螃`, `bar蟹`
+ ///
+ /// ### Example
+ /// ```no_run
+ /// struct Cake {
+ /// cake_sugar: u8,
+ /// cake_flour: u8,
+ /// cake_eggs: u8
+ /// }
+ /// ```
+ /// Use instead:
+ /// ```no_run
+ /// struct Cake {
+ /// sugar: u8,
+ /// flour: u8,
+ /// eggs: u8
+ /// }
+ /// ```
+ #[clippy::version = "1.75.0"]
+ pub STRUCT_FIELD_NAMES,
+ pedantic,
+ "structs where all fields share a prefix/postfix or contain the name of the struct"
+}
-pub struct EnumVariantNames {
+pub struct ItemNameRepetitions {
modules: Vec<(Symbol, String, OwnerId)>,
- threshold: u64,
+ enum_threshold: u64,
+ struct_threshold: u64,
avoid_breaking_exported_api: bool,
allow_private_module_inception: bool,
}
-impl EnumVariantNames {
+impl ItemNameRepetitions {
#[must_use]
- pub fn new(threshold: u64, avoid_breaking_exported_api: bool, allow_private_module_inception: bool) -> Self {
+ pub fn new(
+ enum_threshold: u64,
+ struct_threshold: u64,
+ avoid_breaking_exported_api: bool,
+ allow_private_module_inception: bool,
+ ) -> Self {
Self {
modules: Vec::new(),
- threshold,
+ enum_threshold,
+ struct_threshold,
avoid_breaking_exported_api,
allow_private_module_inception,
}
}
}
-impl_lint_pass!(EnumVariantNames => [
+impl_lint_pass!(ItemNameRepetitions => [
ENUM_VARIANT_NAMES,
+ STRUCT_FIELD_NAMES,
MODULE_NAME_REPETITIONS,
MODULE_INCEPTION
]);
+#[must_use]
+fn have_no_extra_prefix(prefixes: &[&str]) -> bool {
+ prefixes.iter().all(|p| p == &"" || p == &"_")
+}
+
+fn check_fields(cx: &LateContext<'_>, threshold: u64, item: &Item<'_>, fields: &[FieldDef<'_>]) {
+ if (fields.len() as u64) < threshold {
+ return;
+ }
+
+ check_struct_name_repetition(cx, item, fields);
+
+ // if the SyntaxContext of the identifiers of the fields and struct differ dont lint them.
+ // this prevents linting in macros in which the location of the field identifier names differ
+ if !fields.iter().all(|field| item.ident.span.eq_ctxt(field.ident.span)) {
+ return;
+ }
+
+ let mut pre: Vec<&str> = match fields.first() {
+ Some(first_field) => first_field.ident.name.as_str().split('_').collect(),
+ None => return,
+ };
+ let mut post = pre.clone();
+ post.reverse();
+ for field in fields {
+ let field_split: Vec<&str> = field.ident.name.as_str().split('_').collect();
+ if field_split.len() == 1 {
+ return;
+ }
+
+ pre = pre
+ .into_iter()
+ .zip(field_split.iter())
+ .take_while(|(a, b)| &a == b)
+ .map(|e| e.0)
+ .collect();
+ post = post
+ .into_iter()
+ .zip(field_split.iter().rev())
+ .take_while(|(a, b)| &a == b)
+ .map(|e| e.0)
+ .collect();
+ }
+ let prefix = pre.join("_");
+ post.reverse();
+ let postfix = match post.last() {
+ Some(&"") => post.join("_") + "_",
+ Some(_) | None => post.join("_"),
+ };
+ if fields.len() > 1 {
+ let (what, value) = match (
+ prefix.is_empty() || prefix.chars().all(|c| c == '_'),
+ postfix.is_empty(),
+ ) {
+ (true, true) => return,
+ (false, _) => ("pre", prefix),
+ (true, false) => ("post", postfix),
+ };
+ span_lint_and_help(
+ cx,
+ STRUCT_FIELD_NAMES,
+ item.span,
+ &format!("all fields have the same {what}fix: `{value}`"),
+ None,
+ &format!("remove the {what}fixes"),
+ );
+ }
+}
+
+fn check_struct_name_repetition(cx: &LateContext<'_>, item: &Item<'_>, fields: &[FieldDef<'_>]) {
+ let snake_name = to_snake_case(item.ident.name.as_str());
+ let item_name_words: Vec<&str> = snake_name.split('_').collect();
+ for field in fields {
+ if field.ident.span.eq_ctxt(item.ident.span) {
+ //consider linting only if the field identifier has the same SyntaxContext as the item(struct)
+ let field_words: Vec<&str> = field.ident.name.as_str().split('_').collect();
+ if field_words.len() >= item_name_words.len() {
+ // if the field name is shorter than the struct name it cannot contain it
+ if field_words.iter().zip(item_name_words.iter()).all(|(a, b)| a == b) {
+ span_lint_hir(
+ cx,
+ STRUCT_FIELD_NAMES,
+ field.hir_id,
+ field.span,
+ "field name starts with the struct's name",
+ );
+ }
+ if field_words.len() > item_name_words.len() {
+ // lint only if the end is not covered by the start
+ if field_words
+ .iter()
+ .rev()
+ .zip(item_name_words.iter().rev())
+ .all(|(a, b)| a == b)
+ {
+ span_lint_hir(
+ cx,
+ STRUCT_FIELD_NAMES,
+ field.hir_id,
+ field.span,
+ "field name ends with the struct's name",
+ );
+ }
+ }
+ }
+ }
+ }
+}
+
fn check_enum_start(cx: &LateContext<'_>, item_name: &str, variant: &Variant<'_>) {
let name = variant.ident.name.as_str();
let item_name_chars = item_name.chars().count();
@@ -167,6 +320,11 @@ fn check_variant(cx: &LateContext<'_>, threshold: u64, def: &EnumDef<'_>, item_n
return;
}
+ for var in def.variants {
+ check_enum_start(cx, item_name, var);
+ check_enum_end(cx, item_name, var);
+ }
+
let first = match def.variants.first() {
Some(variant) => variant.ident.name.as_str(),
None => return,
@@ -175,8 +333,6 @@ fn check_variant(cx: &LateContext<'_>, threshold: u64, def: &EnumDef<'_>, item_n
let mut post = pre.clone();
post.reverse();
for var in def.variants {
- check_enum_start(cx, item_name, var);
- check_enum_end(cx, item_name, var);
let name = var.ident.name.as_str();
let variant_split = camel_case_split(name);
@@ -218,35 +374,7 @@ fn check_variant(cx: &LateContext<'_>, threshold: u64, def: &EnumDef<'_>, item_n
);
}
-#[must_use]
-fn have_no_extra_prefix(prefixes: &[&str]) -> bool {
- prefixes.iter().all(|p| p == &"" || p == &"_")
-}
-
-#[must_use]
-fn to_camel_case(item_name: &str) -> String {
- let mut s = String::new();
- let mut up = true;
- for c in item_name.chars() {
- if c.is_uppercase() {
- // we only turn snake case text into CamelCase
- return item_name.to_string();
- }
- if c == '_' {
- up = true;
- continue;
- }
- if up {
- up = false;
- s.extend(c.to_uppercase());
- } else {
- s.push(c);
- }
- }
- s
-}
-
-impl LateLintPass<'_> for EnumVariantNames {
+impl LateLintPass<'_> for ItemNameRepetitions {
fn check_item_post(&mut self, _cx: &LateContext<'_>, _item: &Item<'_>) {
let last = self.modules.pop();
assert!(last.is_some());
@@ -303,9 +431,15 @@ impl LateLintPass<'_> for EnumVariantNames {
}
}
}
- if let ItemKind::Enum(ref def, _) = item.kind {
- if !(self.avoid_breaking_exported_api && cx.effective_visibilities.is_exported(item.owner_id.def_id)) {
- check_variant(cx, self.threshold, def, item_name, item.span);
+ if !(self.avoid_breaking_exported_api && cx.effective_visibilities.is_exported(item.owner_id.def_id))
+ && span_is_local(item.span)
+ {
+ match item.kind {
+ ItemKind::Enum(def, _) => check_variant(cx, self.enum_threshold, &def, item_name, item.span),
+ ItemKind::Struct(VariantData::Struct(fields, _), _) => {
+ check_fields(cx, self.struct_threshold, item, fields);
+ },
+ _ => (),
}
}
self.modules.push((item.ident.name, item_camel, item.owner_id));
diff --git a/src/tools/clippy/clippy_lints/src/items_after_statements.rs b/src/tools/clippy/clippy_lints/src/items_after_statements.rs
index a7ec57e28..9605d76fb 100644
--- a/src/tools/clippy/clippy_lints/src/items_after_statements.rs
+++ b/src/tools/clippy/clippy_lints/src/items_after_statements.rs
@@ -16,7 +16,7 @@ declare_clippy_lint! {
/// it's hard to figure out which item is meant in a statement.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// fn foo() {
/// println!("cake");
/// }
@@ -31,7 +31,7 @@ declare_clippy_lint! {
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// fn foo() {
/// println!("cake");
/// }
diff --git a/src/tools/clippy/clippy_lints/src/items_after_test_module.rs b/src/tools/clippy/clippy_lints/src/items_after_test_module.rs
index 55a43e915..35e01862c 100644
--- a/src/tools/clippy/clippy_lints/src/items_after_test_module.rs
+++ b/src/tools/clippy/clippy_lints/src/items_after_test_module.rs
@@ -1,10 +1,12 @@
-use clippy_utils::diagnostics::span_lint_and_help;
-use clippy_utils::{is_from_proc_macro, is_in_cfg_test};
-use rustc_hir::{HirId, ItemId, ItemKind, Mod};
-use rustc_lint::{LateContext, LateLintPass, LintContext};
-use rustc_middle::lint::in_external_macro;
+use clippy_utils::diagnostics::span_lint_hir_and_then;
+use clippy_utils::source::snippet_opt;
+use clippy_utils::{fulfill_or_allowed, is_cfg_test, is_from_proc_macro};
+use rustc_errors::{Applicability, SuggestionStyle};
+use rustc_hir::{HirId, Item, ItemKind, Mod};
+use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::{sym, Span};
+use rustc_span::hygiene::AstPass;
+use rustc_span::{sym, ExpnKind};
declare_clippy_lint! {
/// ### What it does
@@ -12,7 +14,7 @@ declare_clippy_lint! {
/// ### Why is this bad?
/// Having items declared after the testing module is confusing and may lead to bad test coverage.
/// ### Example
- /// ```rust
+ /// ```no_run
/// #[cfg(test)]
/// mod tests {
/// // [...]
@@ -23,7 +25,7 @@ declare_clippy_lint! {
/// }
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// fn my_function() {
/// // [...]
/// }
@@ -41,46 +43,67 @@ declare_clippy_lint! {
declare_lint_pass!(ItemsAfterTestModule => [ITEMS_AFTER_TEST_MODULE]);
-impl LateLintPass<'_> for ItemsAfterTestModule {
- fn check_mod(&mut self, cx: &LateContext<'_>, _: &Mod<'_>, _: HirId) {
- let mut was_test_mod_visited = false;
- let mut test_mod_span: Option<Span> = None;
+fn cfg_test_module<'tcx>(cx: &LateContext<'tcx>, item: &Item<'tcx>) -> bool {
+ if let ItemKind::Mod(test_mod) = item.kind
+ && item.span.hi() == test_mod.spans.inner_span.hi()
+ && is_cfg_test(cx.tcx, item.hir_id())
+ && !item.span.from_expansion()
+ && !is_from_proc_macro(cx, item)
+ {
+ true
+ } else {
+ false
+ }
+}
- let hir = cx.tcx.hir();
- let items = hir.items().collect::<Vec<ItemId>>();
+impl LateLintPass<'_> for ItemsAfterTestModule {
+ fn check_mod(&mut self, cx: &LateContext<'_>, module: &Mod<'_>, _: HirId) {
+ let mut items = module.item_ids.iter().map(|&id| cx.tcx.hir().item(id));
- for (i, itid) in items.iter().enumerate() {
- let item = hir.item(*itid);
+ let Some((mod_pos, test_mod)) = items.by_ref().enumerate().find(|(_, item)| cfg_test_module(cx, item)) else {
+ return;
+ };
- if_chain! {
- if was_test_mod_visited;
- if i == (items.len() - 3 /* Weird magic number (HIR-translation behaviour) */);
- if cx.sess().source_map().lookup_char_pos(item.span.lo()).file.name_hash
- == cx.sess().source_map().lookup_char_pos(test_mod_span.unwrap().lo()).file.name_hash; // Will never fail
- if !matches!(item.kind, ItemKind::Mod(_));
- if !is_in_cfg_test(cx.tcx, itid.hir_id()); // The item isn't in the testing module itself
- if !in_external_macro(cx.sess(), item.span);
- if !is_from_proc_macro(cx, item);
+ let after: Vec<_> = items
+ .filter(|item| {
+ // Ignore the generated test main function
+ !(item.ident.name == sym::main
+ && item.span.ctxt().outer_expn_data().kind == ExpnKind::AstPass(AstPass::TestHarness))
+ })
+ .collect();
- then {
- span_lint_and_help(cx, ITEMS_AFTER_TEST_MODULE, test_mod_span.unwrap().with_hi(item.span.hi()), "items were found after the testing module", None, "move the items to before the testing module was defined");
- }};
+ if let Some(last) = after.last()
+ && after.iter().all(|&item| {
+ !matches!(item.kind, ItemKind::Mod(_)) && !item.span.from_expansion() && !is_from_proc_macro(cx, item)
+ })
+ && !fulfill_or_allowed(cx, ITEMS_AFTER_TEST_MODULE, after.iter().map(|item| item.hir_id()))
+ {
+ let def_spans: Vec<_> = std::iter::once(test_mod.owner_id)
+ .chain(after.iter().map(|item| item.owner_id))
+ .map(|id| cx.tcx.def_span(id))
+ .collect();
- if let ItemKind::Mod(module) = item.kind && item.span.hi() == module.spans.inner_span.hi() {
- // Check that it works the same way, the only I way I've found for #10713
- for attr in cx.tcx.get_attrs(item.owner_id.to_def_id(), sym::cfg) {
- if_chain! {
- if attr.has_name(sym::cfg);
- if let Some(mitems) = attr.meta_item_list();
- if let [mitem] = &*mitems;
- if mitem.has_name(sym::test);
- then {
- was_test_mod_visited = true;
- test_mod_span = Some(item.span);
- }
+ span_lint_hir_and_then(
+ cx,
+ ITEMS_AFTER_TEST_MODULE,
+ test_mod.hir_id(),
+ def_spans,
+ "items after a test module",
+ |diag| {
+ if let Some(prev) = mod_pos.checked_sub(1)
+ && let prev = cx.tcx.hir().item(module.item_ids[prev])
+ && let items_span = last.span.with_lo(test_mod.span.hi())
+ && let Some(items) = snippet_opt(cx, items_span)
+ {
+ diag.multipart_suggestion_with_style(
+ "move the items to before the test module was defined",
+ vec![(prev.span.shrink_to_hi(), items), (items_span, String::new())],
+ Applicability::MachineApplicable,
+ SuggestionStyle::HideCodeAlways,
+ );
}
- }
- }
+ },
+ );
}
}
}
diff --git a/src/tools/clippy/clippy_lints/src/iter_not_returning_iterator.rs b/src/tools/clippy/clippy_lints/src/iter_not_returning_iterator.rs
index 066d2c4b7..505aadd1a 100644
--- a/src/tools/clippy/clippy_lints/src/iter_not_returning_iterator.rs
+++ b/src/tools/clippy/clippy_lints/src/iter_not_returning_iterator.rs
@@ -15,7 +15,7 @@ declare_clippy_lint! {
/// Methods named `iter` or `iter_mut` conventionally return an `Iterator`.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// // `String` does not implement `Iterator`
/// struct Data {}
/// impl Data {
@@ -25,7 +25,7 @@ declare_clippy_lint! {
/// }
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// use std::str::Chars;
/// struct Data {}
/// impl Data {
diff --git a/src/tools/clippy/clippy_lints/src/iter_without_into_iter.rs b/src/tools/clippy/clippy_lints/src/iter_without_into_iter.rs
new file mode 100644
index 000000000..3c291f255
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/iter_without_into_iter.rs
@@ -0,0 +1,295 @@
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::get_parent_as_impl;
+use clippy_utils::source::snippet;
+use clippy_utils::ty::{implements_trait, make_normalized_projection};
+use rustc_ast::Mutability;
+use rustc_errors::Applicability;
+use rustc_hir::{FnRetTy, ImplItemKind, ImplicitSelfKind, ItemKind, TyKind};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::ty::{self, Ty};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::{sym, Symbol};
+use std::iter;
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Looks for `iter` and `iter_mut` methods without an associated `IntoIterator for (&|&mut) Type` implementation.
+ ///
+ /// ### Why is this bad?
+ /// It's not bad, but having them is idiomatic and allows the type to be used in for loops directly
+ /// (`for val in &iter {}`), without having to first call `iter()` or `iter_mut()`.
+ ///
+ /// ### Limitations
+ /// This lint focuses on providing an idiomatic API. Therefore, it will only
+ /// lint on types which are accessible outside of the crate. For internal types,
+ /// the `IntoIterator` trait can be implemented on demand if it is actually needed.
+ ///
+ /// ### Example
+ /// ```no_run
+ /// struct MySlice<'a>(&'a [u8]);
+ /// impl<'a> MySlice<'a> {
+ /// pub fn iter(&self) -> std::slice::Iter<'a, u8> {
+ /// self.0.iter()
+ /// }
+ /// }
+ /// ```
+ /// Use instead:
+ /// ```no_run
+ /// struct MySlice<'a>(&'a [u8]);
+ /// impl<'a> MySlice<'a> {
+ /// pub fn iter(&self) -> std::slice::Iter<'a, u8> {
+ /// self.0.iter()
+ /// }
+ /// }
+ /// impl<'a> IntoIterator for &MySlice<'a> {
+ /// type Item = &'a u8;
+ /// type IntoIter = std::slice::Iter<'a, u8>;
+ /// fn into_iter(self) -> Self::IntoIter {
+ /// self.iter()
+ /// }
+ /// }
+ /// ```
+ #[clippy::version = "1.74.0"]
+ pub ITER_WITHOUT_INTO_ITER,
+ pedantic,
+ "implementing `iter(_mut)` without an associated `IntoIterator for (&|&mut) Type` impl"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// This is the opposite of the `iter_without_into_iter` lint.
+ /// It looks for `IntoIterator for (&|&mut) Type` implementations without an inherent `iter` or `iter_mut` method
+ /// on the type or on any of the types in its `Deref` chain.
+ ///
+ /// ### Why is this bad?
+ /// It's not bad, but having them is idiomatic and allows the type to be used in iterator chains
+ /// by just calling `.iter()`, instead of the more awkward `<&Type>::into_iter` or `(&val).into_iter()` syntax
+ /// in case of ambiguity with another `IntoIterator` impl.
+ ///
+ /// ### Limitations
+ /// This lint focuses on providing an idiomatic API. Therefore, it will only
+ /// lint on types which are accessible outside of the crate. For internal types,
+ /// these methods can be added on demand if they are actually needed. Otherwise,
+ /// it would trigger the [`dead_code`] lint for the unused method.
+ ///
+ /// [`dead_code`]: https://doc.rust-lang.org/rustc/lints/listing/warn-by-default.html#dead-code
+ ///
+ /// ### Example
+ /// ```no_run
+ /// struct MySlice<'a>(&'a [u8]);
+ /// impl<'a> IntoIterator for &MySlice<'a> {
+ /// type Item = &'a u8;
+ /// type IntoIter = std::slice::Iter<'a, u8>;
+ /// fn into_iter(self) -> Self::IntoIter {
+ /// self.0.iter()
+ /// }
+ /// }
+ /// ```
+ /// Use instead:
+ /// ```no_run
+ /// struct MySlice<'a>(&'a [u8]);
+ /// impl<'a> MySlice<'a> {
+ /// pub fn iter(&self) -> std::slice::Iter<'a, u8> {
+ /// self.into_iter()
+ /// }
+ /// }
+ /// impl<'a> IntoIterator for &MySlice<'a> {
+ /// type Item = &'a u8;
+ /// type IntoIter = std::slice::Iter<'a, u8>;
+ /// fn into_iter(self) -> Self::IntoIter {
+ /// self.0.iter()
+ /// }
+ /// }
+ /// ```
+ #[clippy::version = "1.74.0"]
+ pub INTO_ITER_WITHOUT_ITER,
+ pedantic,
+ "implementing `IntoIterator for (&|&mut) Type` without an inherent `iter(_mut)` method"
+}
+
+declare_lint_pass!(IterWithoutIntoIter => [ITER_WITHOUT_INTO_ITER, INTO_ITER_WITHOUT_ITER]);
+
+/// Checks if a given type is nameable in a trait (impl).
+/// RPIT is stable, but impl Trait in traits is not (yet), so when we have
+/// a function such as `fn iter(&self) -> impl IntoIterator`, we can't
+/// suggest `type IntoIter = impl IntoIterator`.
+fn is_nameable_in_impl_trait(ty: &rustc_hir::Ty<'_>) -> bool {
+ !matches!(ty.kind, TyKind::OpaqueDef(..))
+}
+
+fn is_ty_exported(cx: &LateContext<'_>, ty: Ty<'_>) -> bool {
+ ty.ty_adt_def()
+ .and_then(|adt| adt.did().as_local())
+ .is_some_and(|did| cx.effective_visibilities.is_exported(did))
+}
+
+/// Returns the deref chain of a type, starting with the type itself.
+fn deref_chain<'cx, 'tcx>(cx: &'cx LateContext<'tcx>, ty: Ty<'tcx>) -> impl Iterator<Item = Ty<'tcx>> + 'cx {
+ iter::successors(Some(ty), |&ty| {
+ if let Some(deref_did) = cx.tcx.lang_items().deref_trait()
+ && implements_trait(cx, ty, deref_did, &[])
+ {
+ make_normalized_projection(cx.tcx, cx.param_env, deref_did, sym::Target, [ty])
+ } else {
+ None
+ }
+ })
+}
+
+fn adt_has_inherent_method(cx: &LateContext<'_>, ty: Ty<'_>, method_name: Symbol) -> bool {
+ if let Some(ty_did) = ty.ty_adt_def().map(ty::AdtDef::did) {
+ cx.tcx.inherent_impls(ty_did).iter().any(|&did| {
+ cx.tcx
+ .associated_items(did)
+ .filter_by_name_unhygienic(method_name)
+ .next()
+ .is_some_and(|item| item.kind == ty::AssocKind::Fn)
+ })
+ } else {
+ false
+ }
+}
+
+impl LateLintPass<'_> for IterWithoutIntoIter {
+ fn check_item(&mut self, cx: &LateContext<'_>, item: &rustc_hir::Item<'_>) {
+ if let ItemKind::Impl(imp) = item.kind
+ && let TyKind::Ref(_, self_ty_without_ref) = &imp.self_ty.kind
+ && let Some(trait_ref) = imp.of_trait
+ && trait_ref
+ .trait_def_id()
+ .is_some_and(|did| cx.tcx.is_diagnostic_item(sym::IntoIterator, did))
+ && let &ty::Ref(_, ty, mtbl) = cx.tcx.type_of(item.owner_id).instantiate_identity().kind()
+ && let expected_method_name = match mtbl {
+ Mutability::Mut => sym::iter_mut,
+ Mutability::Not => sym::iter,
+ }
+ && !deref_chain(cx, ty).any(|ty| {
+ // We can't check inherent impls for slices, but we know that they have an `iter(_mut)` method
+ ty.peel_refs().is_slice() || adt_has_inherent_method(cx, ty, expected_method_name)
+ })
+ && let Some(iter_assoc_span) = imp.items.iter().find_map(|item| {
+ if item.ident.name == sym!(IntoIter) {
+ Some(cx.tcx.hir().impl_item(item.id).expect_type().span)
+ } else {
+ None
+ }
+ })
+ && is_ty_exported(cx, ty)
+ {
+ span_lint_and_then(
+ cx,
+ INTO_ITER_WITHOUT_ITER,
+ item.span,
+ &format!("`IntoIterator` implemented for a reference type without an `{expected_method_name}` method"),
+ |diag| {
+ // The suggestion forwards to the `IntoIterator` impl and uses a form of UFCS
+ // to avoid name ambiguities, as there might be an inherent into_iter method
+ // that we don't want to call.
+ let sugg = format!(
+ "
+impl {self_ty_without_ref} {{
+ fn {expected_method_name}({ref_self}self) -> {iter_ty} {{
+ <{ref_self}Self as IntoIterator>::into_iter(self)
+ }}
+}}
+",
+ self_ty_without_ref = snippet(cx, self_ty_without_ref.ty.span, ".."),
+ ref_self = mtbl.ref_prefix_str(),
+ iter_ty = snippet(cx, iter_assoc_span, ".."),
+ );
+
+ diag.span_suggestion_verbose(
+ item.span.shrink_to_lo(),
+ format!("consider implementing `{expected_method_name}`"),
+ sugg,
+ // Just like iter_without_into_iter, this suggestion is on a best effort basis
+ // and requires potentially adding lifetimes or moving them around.
+ Applicability::Unspecified,
+ );
+ },
+ );
+ }
+ }
+
+ fn check_impl_item(&mut self, cx: &LateContext<'_>, item: &rustc_hir::ImplItem<'_>) {
+ let item_did = item.owner_id.to_def_id();
+ let (borrow_prefix, expected_implicit_self) = match item.ident.name {
+ sym::iter => ("&", ImplicitSelfKind::ImmRef),
+ sym::iter_mut => ("&mut ", ImplicitSelfKind::MutRef),
+ _ => return,
+ };
+
+ if let ImplItemKind::Fn(sig, _) = item.kind
+ && let FnRetTy::Return(ret) = sig.decl.output
+ && is_nameable_in_impl_trait(ret)
+ && cx.tcx.generics_of(item_did).params.is_empty()
+ && sig.decl.implicit_self == expected_implicit_self
+ && sig.decl.inputs.len() == 1
+ && let Some(imp) = get_parent_as_impl(cx.tcx, item.hir_id())
+ && imp.of_trait.is_none()
+ && let sig = cx.tcx.liberate_late_bound_regions(
+ item_did,
+ cx.tcx.fn_sig(item_did).instantiate_identity()
+ )
+ && let ref_ty = sig.inputs()[0]
+ && let Some(into_iter_did) = cx.tcx.get_diagnostic_item(sym::IntoIterator)
+ && let Some(iterator_did) = cx.tcx.get_diagnostic_item(sym::Iterator)
+ && let ret_ty = sig.output()
+ // Order is important here, we need to check that the `fn iter` return type actually implements `IntoIterator`
+ // *before* normalizing `<_ as IntoIterator>::Item` (otherwise make_normalized_projection ICEs)
+ && implements_trait(cx, ret_ty, iterator_did, &[])
+ && let Some(iter_ty) = make_normalized_projection(
+ cx.tcx,
+ cx.param_env,
+ iterator_did,
+ sym::Item,
+ [ret_ty],
+ )
+ // Only lint if the `IntoIterator` impl doesn't actually exist
+ && !implements_trait(cx, ref_ty, into_iter_did, &[])
+ && is_ty_exported(cx, ref_ty.peel_refs())
+ {
+ let self_ty_snippet = format!("{borrow_prefix}{}", snippet(cx, imp.self_ty.span, ".."));
+
+ span_lint_and_then(
+ cx,
+ ITER_WITHOUT_INTO_ITER,
+ item.span,
+ &format!(
+ "`{}` method without an `IntoIterator` impl for `{self_ty_snippet}`",
+ item.ident
+ ),
+ |diag| {
+ // Get the lower span of the `impl` block, and insert the suggestion right before it:
+ // impl X {
+ // ^ fn iter(&self) -> impl IntoIterator { ... }
+ // }
+ let span_behind_impl = cx
+ .tcx
+ .def_span(cx.tcx.hir().parent_id(item.hir_id()).owner.def_id)
+ .shrink_to_lo();
+
+ let sugg = format!(
+ "
+impl IntoIterator for {self_ty_snippet} {{
+ type IntoIter = {ret_ty};
+ type Item = {iter_ty};
+ fn into_iter(self) -> Self::IntoIter {{
+ self.iter()
+ }}
+}}
+"
+ );
+ diag.span_suggestion_verbose(
+ span_behind_impl,
+ format!("consider implementing `IntoIterator` for `{self_ty_snippet}`"),
+ sugg,
+ // Suggestion is on a best effort basis, might need some adjustments by the user
+ // such as adding some lifetimes in the associated types, or importing types.
+ Applicability::Unspecified,
+ );
+ },
+ );
+ }
+ }
+}
diff --git a/src/tools/clippy/clippy_lints/src/large_enum_variant.rs b/src/tools/clippy/clippy_lints/src/large_enum_variant.rs
index b22b57a30..0bf9b8718 100644
--- a/src/tools/clippy/clippy_lints/src/large_enum_variant.rs
+++ b/src/tools/clippy/clippy_lints/src/large_enum_variant.rs
@@ -9,7 +9,7 @@ use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::lint::in_external_macro;
use rustc_middle::ty::{Adt, Ty};
use rustc_session::{declare_tool_lint, impl_lint_pass};
-use rustc_span::source_map::Span;
+use rustc_span::Span;
declare_clippy_lint! {
/// ### What it does
@@ -38,7 +38,7 @@ declare_clippy_lint! {
/// this may lead to a false positive.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// enum Test {
/// A(i32),
/// B([i32; 8000]),
@@ -46,7 +46,7 @@ declare_clippy_lint! {
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// // Possibly better
/// enum Test2 {
/// A(i32),
diff --git a/src/tools/clippy/clippy_lints/src/large_futures.rs b/src/tools/clippy/clippy_lints/src/large_futures.rs
index 19f1e08b5..26a727852 100644
--- a/src/tools/clippy/clippy_lints/src/large_futures.rs
+++ b/src/tools/clippy/clippy_lints/src/large_futures.rs
@@ -12,11 +12,11 @@ declare_clippy_lint! {
/// It checks for the size of a `Future` created by `async fn` or `async {}`.
///
/// ### Why is this bad?
- /// Due to the current [unideal implementation](https://github.com/rust-lang/rust/issues/69826) of `Generator`,
+ /// Due to the current [unideal implementation](https://github.com/rust-lang/rust/issues/69826) of `Coroutine`,
/// large size of a `Future` may cause stack overflows.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// async fn large_future(_x: [u8; 16 * 1024]) {}
///
/// pub async fn trigger() {
@@ -26,7 +26,7 @@ declare_clippy_lint! {
///
/// `Box::pin` the big future instead.
///
- /// ```rust
+ /// ```no_run
/// async fn large_future(_x: [u8; 16 * 1024]) {}
///
/// pub async fn trigger() {
diff --git a/src/tools/clippy/clippy_lints/src/large_stack_arrays.rs b/src/tools/clippy/clippy_lints/src/large_stack_arrays.rs
index 0a5901bce..5e312ab72 100644
--- a/src/tools/clippy/clippy_lints/src/large_stack_arrays.rs
+++ b/src/tools/clippy/clippy_lints/src/large_stack_arrays.rs
@@ -39,27 +39,35 @@ impl_lint_pass!(LargeStackArrays => [LARGE_STACK_ARRAYS]);
impl<'tcx> LateLintPass<'tcx> for LargeStackArrays {
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
if let ExprKind::Repeat(_, _) | ExprKind::Array(_) = expr.kind
- && let ty::Array(element_type, cst) = cx.typeck_results().expr_ty(expr).kind()
- && let ConstKind::Value(ty::ValTree::Leaf(element_count)) = cst.kind()
- && let Ok(element_count) = element_count.try_to_target_usize(cx.tcx)
- && let Ok(element_size) = cx.layout_of(*element_type).map(|l| l.size.bytes())
- && !cx.tcx.hir().parent_iter(expr.hir_id)
- .any(|(_, node)| matches!(node, Node::Item(Item { kind: ItemKind::Static(..), .. })))
- && self.maximum_allowed_size < u128::from(element_count) * u128::from(element_size) {
- span_lint_and_help(
- cx,
- LARGE_STACK_ARRAYS,
- expr.span,
- &format!(
- "allocating a local array larger than {} bytes",
- self.maximum_allowed_size
- ),
- None,
- &format!(
- "consider allocating on the heap with `vec!{}.into_boxed_slice()`",
- snippet(cx, expr.span, "[...]")
- ),
- );
- }
+ && let ty::Array(element_type, cst) = cx.typeck_results().expr_ty(expr).kind()
+ && let ConstKind::Value(ty::ValTree::Leaf(element_count)) = cst.kind()
+ && let Ok(element_count) = element_count.try_to_target_usize(cx.tcx)
+ && let Ok(element_size) = cx.layout_of(*element_type).map(|l| l.size.bytes())
+ && !cx.tcx.hir().parent_iter(expr.hir_id).any(|(_, node)| {
+ matches!(
+ node,
+ Node::Item(Item {
+ kind: ItemKind::Static(..),
+ ..
+ })
+ )
+ })
+ && self.maximum_allowed_size < u128::from(element_count) * u128::from(element_size)
+ {
+ span_lint_and_help(
+ cx,
+ LARGE_STACK_ARRAYS,
+ expr.span,
+ &format!(
+ "allocating a local array larger than {} bytes",
+ self.maximum_allowed_size
+ ),
+ None,
+ &format!(
+ "consider allocating on the heap with `vec!{}.into_boxed_slice()`",
+ snippet(cx, expr.span, "[...]")
+ ),
+ );
+ }
}
}
diff --git a/src/tools/clippy/clippy_lints/src/large_stack_frames.rs b/src/tools/clippy/clippy_lints/src/large_stack_frames.rs
index 1d28d7dd0..33636eb68 100644
--- a/src/tools/clippy/clippy_lints/src/large_stack_frames.rs
+++ b/src/tools/clippy/clippy_lints/src/large_stack_frames.rs
@@ -49,7 +49,7 @@ declare_clippy_lint! {
/// ### Example
/// This function creates four 500 KB arrays on the stack. Quite big but just small enough to not trigger `large_stack_arrays`.
/// However, looking at the function as a whole, it's clear that this uses a lot of stack space.
- /// ```rust
+ /// ```no_run
/// struct QuiteLargeType([u8; 500_000]);
/// fn foo() {
/// // ... some function that uses a lot of stack space ...
@@ -62,7 +62,7 @@ declare_clippy_lint! {
///
/// Instead of doing this, allocate the arrays on the heap.
/// This currently requires going through a `Vec` first and then converting it to a `Box`:
- /// ```rust
+ /// ```no_run
/// struct NotSoLargeType(Box<[u8]>);
///
/// fn foo() {
diff --git a/src/tools/clippy/clippy_lints/src/len_zero.rs b/src/tools/clippy/clippy_lints/src/len_zero.rs
index c06b35ca0..0f17d2676 100644
--- a/src/tools/clippy/clippy_lints/src/len_zero.rs
+++ b/src/tools/clippy/clippy_lints/src/len_zero.rs
@@ -15,7 +15,8 @@ use rustc_hir::{
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::{self, AssocKind, FnSig, Ty};
use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::source_map::{Span, Spanned, Symbol};
+use rustc_span::{Span, Symbol};
+use rustc_span::source_map::Spanned;
use rustc_span::symbol::sym;
declare_clippy_lint! {
@@ -181,8 +182,7 @@ impl<'tcx> LateLintPass<'tcx> for LenZero {
let mut applicability = Applicability::MachineApplicable;
let lit1 = peel_ref_operators(cx, lt.init);
- let lit_str =
- Sugg::hir_with_context(cx, lit1, lt.span.ctxt(), "_", &mut applicability).maybe_par();
+ let lit_str = Sugg::hir_with_context(cx, lit1, lt.span.ctxt(), "_", &mut applicability).maybe_par();
span_lint_and_sugg(
cx,
@@ -288,18 +288,26 @@ enum LenOutput {
}
fn extract_future_output<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<&'tcx PathSegment<'tcx>> {
- if let ty::Alias(_, alias_ty) = ty.kind() &&
- let Some(Node::Item(item)) = cx.tcx.hir().get_if_local(alias_ty.def_id) &&
- let Item { kind: ItemKind::OpaqueTy(opaque), .. } = item &&
- opaque.bounds.len() == 1 &&
- let GenericBound::LangItemTrait(LangItem::Future, _, _, generic_args) = &opaque.bounds[0] &&
- generic_args.bindings.len() == 1 &&
- let TypeBindingKind::Equality {
- term: rustc_hir::Term::Ty(rustc_hir::Ty {kind: TyKind::Path(QPath::Resolved(_, path)), .. }),
- } = &generic_args.bindings[0].kind &&
- path.segments.len() == 1 {
- return Some(&path.segments[0]);
- }
+ if let ty::Alias(_, alias_ty) = ty.kind()
+ && let Some(Node::Item(item)) = cx.tcx.hir().get_if_local(alias_ty.def_id)
+ && let Item {
+ kind: ItemKind::OpaqueTy(opaque),
+ ..
+ } = item
+ && opaque.bounds.len() == 1
+ && let GenericBound::LangItemTrait(LangItem::Future, _, _, generic_args) = &opaque.bounds[0]
+ && generic_args.bindings.len() == 1
+ && let TypeBindingKind::Equality {
+ term:
+ rustc_hir::Term::Ty(rustc_hir::Ty {
+ kind: TyKind::Path(QPath::Resolved(_, path)),
+ ..
+ }),
+ } = &generic_args.bindings[0].kind
+ && path.segments.len() == 1
+ {
+ return Some(&path.segments[0]);
+ }
None
}
diff --git a/src/tools/clippy/clippy_lints/src/let_underscore.rs b/src/tools/clippy/clippy_lints/src/let_underscore.rs
index e7c875ab3..04f23a213 100644
--- a/src/tools/clippy/clippy_lints/src/let_underscore.rs
+++ b/src/tools/clippy/clippy_lints/src/let_underscore.rs
@@ -17,7 +17,7 @@ declare_clippy_lint! {
/// expr
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// fn f() -> Result<u32, u32> {
/// Ok(0)
/// }
@@ -69,7 +69,7 @@ declare_clippy_lint! {
/// and ignore the resulting value.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// async fn foo() -> Result<(), ()> {
/// Ok(())
/// }
@@ -77,7 +77,7 @@ declare_clippy_lint! {
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # async fn context() {
/// async fn foo() -> Result<(), ()> {
/// Ok(())
@@ -107,14 +107,14 @@ declare_clippy_lint! {
/// lints.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// fn foo() -> Result<u32, ()> {
/// Ok(123)
/// }
/// let _ = foo();
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// fn foo() -> Result<u32, ()> {
/// Ok(123)
/// }
@@ -159,14 +159,15 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore {
binding or dropping explicitly with `std::mem::drop`",
);
} else if let Some(future_trait_def_id) = cx.tcx.lang_items().future_trait()
- && implements_trait(cx, cx.typeck_results().expr_ty(init), future_trait_def_id, &[]) {
+ && implements_trait(cx, cx.typeck_results().expr_ty(init), future_trait_def_id, &[])
+ {
span_lint_and_help(
cx,
LET_UNDERSCORE_FUTURE,
local.span,
"non-binding `let` on a future",
None,
- "consider awaiting the future or dropping explicitly with `std::mem::drop`"
+ "consider awaiting the future or dropping explicitly with `std::mem::drop`",
);
} else if is_must_use_ty(cx, cx.typeck_results().expr_ty(init)) {
span_lint_and_help(
@@ -203,17 +204,17 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore {
return;
}
- span_lint_and_help(
+ span_lint_and_help(
cx,
LET_UNDERSCORE_UNTYPED,
local.span,
"non-binding `let` without a type annotation",
- Some(
- Span::new(local.pat.span.hi(),
- local.pat.span.hi() + BytePos(1),
- local.pat.span.ctxt(),
- local.pat.span.parent()
- )),
+ Some(Span::new(
+ local.pat.span.hi(),
+ local.pat.span.hi() + BytePos(1),
+ local.pat.span.ctxt(),
+ local.pat.span.parent(),
+ )),
"consider adding a type annotation",
);
}
diff --git a/src/tools/clippy/clippy_lints/src/let_with_type_underscore.rs b/src/tools/clippy/clippy_lints/src/let_with_type_underscore.rs
index 4e9d77ea1..79d728a02 100644
--- a/src/tools/clippy/clippy_lints/src/let_with_type_underscore.rs
+++ b/src/tools/clippy/clippy_lints/src/let_with_type_underscore.rs
@@ -31,7 +31,7 @@ impl LateLintPass<'_> for UnderscoreTyped {
if !in_external_macro(cx.tcx.sess, local.span);
if let Some(ty) = local.ty; // Ensure that it has a type defined
if let TyKind::Infer = &ty.kind; // that type is '_'
- if local.span.ctxt() == ty.span.ctxt();
+ if local.span.eq_ctxt(ty.span);
then {
// NOTE: Using `is_from_proc_macro` on `init` will require that it's initialized,
// this doesn't. Alternatively, `WithSearchPat` can be implemented for `Ty`
diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs
index 1271be2fd..ab978a677 100644
--- a/src/tools/clippy/clippy_lints/src/lib.rs
+++ b/src/tools/clippy/clippy_lints/src/lib.rs
@@ -50,10 +50,6 @@ extern crate clippy_utils;
#[macro_use]
extern crate declare_clippy_lint;
-use std::io;
-use std::path::PathBuf;
-
-use clippy_utils::msrvs::Msrv;
use rustc_data_structures::fx::FxHashSet;
use rustc_lint::{Lint, LintId};
use rustc_session::Session;
@@ -121,7 +117,6 @@ mod empty_structs_with_brackets;
mod endian_bytes;
mod entry;
mod enum_clike;
-mod enum_variants;
mod equatable_if_let;
mod error_impl_error;
mod escape;
@@ -166,9 +161,11 @@ mod inline_fn_without_body;
mod instant_subtraction;
mod int_plus_one;
mod invalid_upcast_comparisons;
+mod item_name_repetitions;
mod items_after_statements;
mod items_after_test_module;
mod iter_not_returning_iterator;
+mod iter_without_into_iter;
mod large_const_arrays;
mod large_enum_variant;
mod large_futures;
@@ -190,6 +187,7 @@ mod manual_async_fn;
mod manual_bits;
mod manual_clamp;
mod manual_float_methods;
+mod manual_hash_one;
mod manual_is_ascii_check;
mod manual_let_else;
mod manual_main_separator_str;
@@ -359,10 +357,7 @@ mod zero_div_zero;
mod zero_sized_map_values;
// end lints modules, do not remove this comment, it’s used in `update_lints`
-use crate::utils::conf::metadata::get_configuration_metadata;
-use crate::utils::conf::TryConf;
-pub use crate::utils::conf::{lookup_conf_file, Conf};
-use crate::utils::FindAll;
+use clippy_config::{get_configuration_metadata, Conf};
/// Register all pre expansion lints
///
@@ -372,65 +367,13 @@ use crate::utils::FindAll;
/// level (i.e `#![cfg_attr(...)]`) will still be expanded even when using a pre-expansion pass.
///
/// Used in `./src/driver.rs`.
-pub fn register_pre_expansion_lints(store: &mut rustc_lint::LintStore, sess: &Session, conf: &Conf) {
+pub fn register_pre_expansion_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
// NOTE: Do not add any more pre-expansion passes. These should be removed eventually.
- let msrv = Msrv::read(&conf.msrv, sess);
- let msrv = move || msrv.clone();
+ let msrv = || conf.msrv.clone();
store.register_pre_expansion_pass(move || Box::new(attrs::EarlyAttributes { msrv: msrv() }));
}
-#[doc(hidden)]
-pub fn read_conf(sess: &Session, path: &io::Result<(Option<PathBuf>, Vec<String>)>) -> Conf {
- if let Ok((_, warnings)) = path {
- for warning in warnings {
- sess.warn(warning.clone());
- }
- }
- let file_name = match path {
- Ok((Some(path), _)) => path,
- Ok((None, _)) => return Conf::default(),
- Err(error) => {
- sess.err(format!("error finding Clippy's configuration file: {error}"));
- return Conf::default();
- },
- };
-
- let TryConf { conf, errors, warnings } = utils::conf::read(sess, file_name);
- // all conf errors are non-fatal, we just use the default conf in case of error
- for error in errors {
- if let Some(span) = error.span {
- sess.span_err(
- span,
- format!("error reading Clippy's configuration file: {}", error.message),
- );
- } else {
- sess.err(format!(
- "error reading Clippy's configuration file `{}`: {}",
- file_name.display(),
- error.message
- ));
- }
- }
-
- for warning in warnings {
- if let Some(span) = warning.span {
- sess.span_warn(
- span,
- format!("error reading Clippy's configuration file: {}", warning.message),
- );
- } else {
- sess.warn(format!(
- "error reading Clippy's configuration file `{}`: {}",
- file_name.display(),
- warning.message
- ));
- }
- }
-
- conf
-}
-
#[derive(Default)]
struct RegistrationGroups {
all: Vec<LintId>,
@@ -516,16 +459,13 @@ pub fn explain(name: &str) -> i32 {
if let Some(info) = declared_lints::LINTS.iter().find(|info| info.lint.name == target) {
println!("{}", info.explanation);
// Check if the lint has configuration
- let mdconf = get_configuration_metadata();
- if let Some(config_vec_positions) = mdconf
- .iter()
- .find_all(|cconf| cconf.lints.contains(&info.lint.name_lower()[8..].to_owned()))
- {
- // If it has, print it
+ let mut mdconf = get_configuration_metadata();
+ let name = name.to_ascii_lowercase();
+ mdconf.retain(|cconf| cconf.lints.contains(&name));
+ if !mdconf.is_empty() {
println!("### Configuration for {}:\n", info.lint.name_lower());
- for position in config_vec_positions {
- let conf = &mdconf[position];
- println!(" - {}: {} (default: {})", conf.name, conf.doc, conf.default);
+ for conf in mdconf {
+ println!("{conf}");
}
}
0
@@ -556,7 +496,7 @@ fn register_categories(store: &mut rustc_lint::LintStore) {
///
/// Used in `./src/driver.rs`.
#[expect(clippy::too_many_lines)]
-pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &Conf) {
+pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &'static Conf) {
register_removed_non_tool_lints(store);
register_categories(store);
@@ -573,7 +513,9 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
// all the internal lints
#[cfg(feature = "internal")]
{
- store.register_early_pass(|| Box::new(utils::internal_lints::clippy_lints_internal::ClippyLintsInternal));
+ store.register_early_pass(|| {
+ Box::new(utils::internal_lints::unsorted_clippy_utils_paths::UnsortedClippyUtilsPaths)
+ });
store.register_early_pass(|| Box::new(utils::internal_lints::produce_ice::ProduceIce));
store.register_late_pass(|_| Box::new(utils::internal_lints::collapsible_calls::CollapsibleCalls));
store.register_late_pass(|_| {
@@ -658,8 +600,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(|_| Box::new(non_octal_unix_permissions::NonOctalUnixPermissions));
store.register_early_pass(|| Box::new(unnecessary_self_imports::UnnecessarySelfImports));
- let msrv = Msrv::read(&conf.msrv, sess);
- let msrv = move || msrv.clone();
+ let msrv = || conf.msrv.clone();
let avoid_breaking_exported_api = conf.avoid_breaking_exported_api;
let allow_expect_in_tests = conf.allow_expect_in_tests;
let allow_unwrap_in_tests = conf.allow_unwrap_in_tests;
@@ -760,6 +701,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
too_many_arguments_threshold,
too_many_lines_threshold,
large_error_threshold,
+ avoid_breaking_exported_api,
))
});
let doc_valid_idents = conf.doc_valid_idents.iter().cloned().collect::<FxHashSet<_>>();
@@ -804,7 +746,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
suppress_restriction_lint_in_const,
))
});
- store.register_late_pass(|_| Box::new(non_copy_const::NonCopyConst));
+ let ignore_interior_mutability = conf.ignore_interior_mutability.clone();
+ store.register_late_pass(move |_| Box::new(non_copy_const::NonCopyConst::new(ignore_interior_mutability.clone())));
store.register_late_pass(|_| Box::new(ptr_offset_with_cast::PtrOffsetWithCast));
store.register_late_pass(|_| Box::new(redundant_clone::RedundantClone));
store.register_late_pass(|_| Box::new(slow_vector_initialization::SlowVectorInit));
@@ -849,10 +792,12 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
))
});
let enum_variant_name_threshold = conf.enum_variant_name_threshold;
+ let struct_field_name_threshold = conf.struct_field_name_threshold;
let allow_private_module_inception = conf.allow_private_module_inception;
store.register_late_pass(move |_| {
- Box::new(enum_variants::EnumVariantNames::new(
+ Box::new(item_name_repetitions::ItemNameRepetitions::new(
enum_variant_name_threshold,
+ struct_field_name_threshold,
avoid_breaking_exported_api,
allow_private_module_inception,
))
@@ -1119,6 +1064,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
msrv(),
))
});
+ store.register_late_pass(move |_| Box::new(manual_hash_one::ManualHashOne::new(msrv())));
+ store.register_late_pass(|_| Box::new(iter_without_into_iter::IterWithoutIntoIter));
// add lints here, do not remove this comment, it's used in `new_lint`
}
diff --git a/src/tools/clippy/clippy_lints/src/lifetimes.rs b/src/tools/clippy/clippy_lints/src/lifetimes.rs
index 0004a150d..7517003be 100644
--- a/src/tools/clippy/clippy_lints/src/lifetimes.rs
+++ b/src/tools/clippy/clippy_lints/src/lifetimes.rs
@@ -20,7 +20,7 @@ use rustc_middle::hir::nested_filter as middle_nested_filter;
use rustc_middle::lint::in_external_macro;
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::def_id::LocalDefId;
-use rustc_span::source_map::Span;
+use rustc_span::Span;
use rustc_span::symbol::{kw, Ident, Symbol};
declare_clippy_lint! {
@@ -38,7 +38,7 @@ declare_clippy_lint! {
/// are mentioned due to potential false positives.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// // Unnecessary lifetime annotations
/// fn in_and_out<'a>(x: &'a u8, y: u8) -> &'a u8 {
/// x
@@ -46,7 +46,7 @@ declare_clippy_lint! {
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// fn elided(x: &u8, y: u8) -> &u8 {
/// x
/// }
@@ -69,7 +69,7 @@ declare_clippy_lint! {
/// them leads to more readable code.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// // unnecessary lifetimes
/// fn unused_lifetime<'a>(x: u8) {
/// // ..
@@ -77,7 +77,7 @@ declare_clippy_lint! {
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// fn no_lifetime(x: u8) {
/// // ...
/// }
@@ -517,9 +517,11 @@ impl<'a, 'tcx> Visitor<'tcx> for RefVisitor<'a, 'tcx> {
fn visit_poly_trait_ref(&mut self, poly_tref: &'tcx PolyTraitRef<'tcx>) {
let trait_ref = &poly_tref.trait_ref;
- if let Some(id) = trait_ref.trait_def_id() && lang_items::FN_TRAITS.iter().any(|&item| {
- self.cx.tcx.lang_items().get(item) == Some(id)
- }) {
+ if let Some(id) = trait_ref.trait_def_id()
+ && lang_items::FN_TRAITS
+ .iter()
+ .any(|&item| self.cx.tcx.lang_items().get(item) == Some(id))
+ {
let mut sub_visitor = RefVisitor::new(self.cx);
sub_visitor.visit_trait_ref(trait_ref);
self.nested_elision_site_lts.append(&mut sub_visitor.all_lts());
diff --git a/src/tools/clippy/clippy_lints/src/lines_filter_map_ok.rs b/src/tools/clippy/clippy_lints/src/lines_filter_map_ok.rs
index 49425ff0a..0a5f5a80c 100644
--- a/src/tools/clippy/clippy_lints/src/lines_filter_map_ok.rs
+++ b/src/tools/clippy/clippy_lints/src/lines_filter_map_ok.rs
@@ -1,5 +1,5 @@
use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::ty::match_type;
+use clippy_utils::ty::is_type_diagnostic_item;
use clippy_utils::{is_diag_item_method, is_trait_method, match_def_path, path_to_local_id, paths};
use rustc_errors::Applicability;
use rustc_hir::{Body, Closure, Expr, ExprKind};
@@ -34,7 +34,7 @@ declare_clippy_lint! {
/// successful results, using `map_while()` would stop at the first error.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # use std::{fs::File, io::{self, BufRead, BufReader}};
/// # let _ = || -> io::Result<()> {
/// let mut lines = BufReader::new(File::open("some-path")?).lines().filter_map(Result::ok);
@@ -43,7 +43,7 @@ declare_clippy_lint! {
/// # Ok(()) };
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # use std::{fs::File, io::{self, BufRead, BufReader}};
/// # let _ = || -> io::Result<()> {
/// let mut lines = BufReader::new(File::open("some-path")?).lines().map_while(Result::ok);
@@ -59,41 +59,56 @@ declare_lint_pass!(LinesFilterMapOk => [LINES_FILTER_MAP_OK]);
impl LateLintPass<'_> for LinesFilterMapOk {
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
- if let ExprKind::MethodCall(fm_method, fm_receiver, [fm_arg], fm_span) = expr.kind &&
- is_trait_method(cx, expr, sym::Iterator) &&
- (fm_method.ident.as_str() == "filter_map" || fm_method.ident.as_str() == "flat_map") &&
- match_type(cx, cx.typeck_results().expr_ty_adjusted(fm_receiver), &paths::STD_IO_LINES)
+ if let ExprKind::MethodCall(fm_method, fm_receiver, [fm_arg], fm_span) = expr.kind
+ && is_trait_method(cx, expr, sym::Iterator)
+ && (fm_method.ident.as_str() == "filter_map" || fm_method.ident.as_str() == "flat_map")
+ && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty_adjusted(fm_receiver), sym::IoLines)
{
let lint = match &fm_arg.kind {
// Detect `Result::ok`
- ExprKind::Path(qpath) =>
- cx.qpath_res(qpath, fm_arg.hir_id).opt_def_id().map(|did|
- match_def_path(cx, did, &paths::CORE_RESULT_OK_METHOD)).unwrap_or_default(),
+ ExprKind::Path(qpath) => cx
+ .qpath_res(qpath, fm_arg.hir_id)
+ .opt_def_id()
+ .map(|did| match_def_path(cx, did, &paths::CORE_RESULT_OK_METHOD))
+ .unwrap_or_default(),
// Detect `|x| x.ok()`
- ExprKind::Closure(Closure { body, .. }) =>
- if let Body { params: [param], value, .. } = cx.tcx.hir().body(*body) &&
- let ExprKind::MethodCall(method, receiver, [], _) = value.kind &&
- path_to_local_id(receiver, param.pat.hir_id) &&
- let Some(method_did) = cx.typeck_results().type_dependent_def_id(value.hir_id)
+ ExprKind::Closure(Closure { body, .. }) => {
+ if let Body {
+ params: [param], value, ..
+ } = cx.tcx.hir().body(*body)
+ && let ExprKind::MethodCall(method, receiver, [], _) = value.kind
+ && path_to_local_id(receiver, param.pat.hir_id)
+ && let Some(method_did) = cx.typeck_results().type_dependent_def_id(value.hir_id)
{
is_diag_item_method(cx, method_did, sym::Result) && method.ident.as_str() == "ok"
} else {
false
}
+ },
_ => false,
};
if lint {
- span_lint_and_then(cx,
+ span_lint_and_then(
+ cx,
LINES_FILTER_MAP_OK,
fm_span,
- &format!("`{}()` will run forever if the iterator repeatedly produces an `Err`", fm_method.ident),
+ &format!(
+ "`{}()` will run forever if the iterator repeatedly produces an `Err`",
+ fm_method.ident
+ ),
|diag| {
diag.span_note(
fm_receiver.span,
"this expression returning a `std::io::Lines` may produce an infinite number of `Err` in case of a read error");
- diag.span_suggestion(fm_span, "replace with", "map_while(Result::ok)", Applicability::MaybeIncorrect);
- });
- }
+ diag.span_suggestion(
+ fm_span,
+ "replace with",
+ "map_while(Result::ok)",
+ Applicability::MaybeIncorrect,
+ );
+ },
+ );
+ }
}
}
}
diff --git a/src/tools/clippy/clippy_lints/src/literal_representation.rs b/src/tools/clippy/clippy_lints/src/literal_representation.rs
index 09ca03173..2c14bb72a 100644
--- a/src/tools/clippy/clippy_lints/src/literal_representation.rs
+++ b/src/tools/clippy/clippy_lints/src/literal_representation.rs
@@ -23,14 +23,14 @@ declare_clippy_lint! {
/// Reading long numbers is difficult without separators.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # let _: u64 =
/// 61864918973511
/// # ;
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # let _: u64 =
/// 61_864_918_973_511
/// # ;
@@ -73,14 +73,14 @@ declare_clippy_lint! {
/// grouped digits.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # let _: u64 =
/// 618_64_9189_73_511
/// # ;
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # let _: u64 =
/// 61_864_918_973_511
/// # ;
@@ -100,7 +100,7 @@ declare_clippy_lint! {
/// Negatively impacts readability.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let x: u32 = 0xFFF_FFF;
/// let y: u8 = 0b01_011_101;
/// ```
@@ -120,7 +120,7 @@ declare_clippy_lint! {
/// Negatively impacts readability.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let x: u64 = 6186491_8973511;
/// ```
#[clippy::version = "pre 1.29.0"]
diff --git a/src/tools/clippy/clippy_lints/src/loops/explicit_iter_loop.rs b/src/tools/clippy/clippy_lints/src/loops/explicit_iter_loop.rs
index 6ab256ef0..1c2b7a169 100644
--- a/src/tools/clippy/clippy_lints/src/loops/explicit_iter_loop.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/explicit_iter_loop.rs
@@ -1,6 +1,6 @@
use super::EXPLICIT_ITER_LOOP;
+use clippy_config::msrvs::{self, Msrv};
use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::ty::{
implements_trait, implements_trait_with_env, is_copy, make_normalized_projection,
@@ -113,7 +113,9 @@ fn is_ref_iterable<'tcx>(
let typeck = cx.typeck_results();
if let Some(trait_id) = cx.tcx.get_diagnostic_item(sym::IntoIterator)
&& let Some(fn_id) = typeck.type_dependent_def_id(call_expr.hir_id)
- && let sig = cx.tcx.liberate_late_bound_regions(fn_id, cx.tcx.fn_sig(fn_id).skip_binder())
+ && let sig = cx
+ .tcx
+ .liberate_late_bound_regions(fn_id, cx.tcx.fn_sig(fn_id).skip_binder())
&& let &[req_self_ty, req_res_ty] = &**sig.inputs_and_output
&& let param_env = cx.tcx.param_env(fn_id)
&& implements_trait_with_env(cx.tcx, param_env, req_self_ty, trait_id, &[])
@@ -131,8 +133,9 @@ fn is_ref_iterable<'tcx>(
return Some((AdjustKind::None, self_ty));
}
- let res_ty = cx.tcx.erase_regions(EarlyBinder::bind(req_res_ty)
- .instantiate(cx.tcx, typeck.node_args(call_expr.hir_id)));
+ let res_ty = cx
+ .tcx
+ .erase_regions(EarlyBinder::bind(req_res_ty).instantiate(cx.tcx, typeck.node_args(call_expr.hir_id)));
let mutbl = if let ty::Ref(_, _, mutbl) = *req_self_ty.kind() {
Some(mutbl)
} else {
@@ -157,7 +160,7 @@ fn is_ref_iterable<'tcx>(
let self_ty = if mutbl.is_mut() {
self_ty
} else {
- Ty::new_ref(cx.tcx,region, TypeAndMut { ty, mutbl })
+ Ty::new_ref(cx.tcx, region, TypeAndMut { ty, mutbl })
};
if implements_trait(cx, self_ty, trait_id, &[])
&& let Some(ty) =
@@ -172,10 +175,7 @@ fn is_ref_iterable<'tcx>(
&& !self_ty.is_ref()
{
// Attempt to borrow
- let self_ty = Ty::new_ref(cx.tcx,cx.tcx.lifetimes.re_erased, TypeAndMut {
- ty: self_ty,
- mutbl,
- });
+ let self_ty = Ty::new_ref(cx.tcx, cx.tcx.lifetimes.re_erased, TypeAndMut { ty: self_ty, mutbl });
if implements_trait(cx, self_ty, trait_id, &[])
&& let Some(ty) = make_normalized_projection(cx.tcx, cx.param_env, trait_id, sym!(IntoIter), [self_ty])
&& ty == res_ty
@@ -187,12 +187,14 @@ fn is_ref_iterable<'tcx>(
match adjustments {
[] => Some((AdjustKind::None, self_ty)),
&[
- Adjustment { kind: Adjust::Deref(_), ..},
+ Adjustment {
+ kind: Adjust::Deref(_), ..
+ },
Adjustment {
kind: Adjust::Borrow(AutoBorrow::Ref(_, mutbl)),
target,
},
- ..
+ ..,
] => {
if enforce_iter_loop_reborrow
&& target != self_ty
@@ -205,8 +207,14 @@ fn is_ref_iterable<'tcx>(
} else {
None
}
- }
- &[Adjustment { kind: Adjust::Deref(_), target }, ..] => {
+ },
+ &[
+ Adjustment {
+ kind: Adjust::Deref(_),
+ target,
+ },
+ ..,
+ ] => {
if is_copy(cx, target)
&& implements_trait(cx, target, trait_id, &[])
&& let Some(ty) =
@@ -217,13 +225,13 @@ fn is_ref_iterable<'tcx>(
} else {
None
}
- }
+ },
&[
Adjustment {
kind: Adjust::Borrow(AutoBorrow::Ref(_, mutbl)),
target,
},
- ..
+ ..,
] => {
if self_ty.is_ref()
&& implements_trait(cx, target, trait_id, &[])
@@ -235,7 +243,7 @@ fn is_ref_iterable<'tcx>(
} else {
None
}
- }
+ },
_ => None,
}
} else {
diff --git a/src/tools/clippy/clippy_lints/src/loops/for_kv_map.rs b/src/tools/clippy/clippy_lints/src/loops/for_kv_map.rs
index ed620460d..94c951fc1 100644
--- a/src/tools/clippy/clippy_lints/src/loops/for_kv_map.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/for_kv_map.rs
@@ -1,9 +1,8 @@
use super::FOR_KV_MAP;
use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then};
use clippy_utils::source::snippet;
-use clippy_utils::sugg;
use clippy_utils::ty::is_type_diagnostic_item;
-use clippy_utils::visitors::is_local_used;
+use clippy_utils::{pat_is_wild, sugg};
use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, Pat, PatKind};
use rustc_lint::LateContext;
use rustc_middle::ty;
@@ -55,12 +54,3 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, arg: &'tcx
}
}
}
-
-/// Returns `true` if the pattern is a `PatWild` or an ident prefixed with `_`.
-fn pat_is_wild<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx PatKind<'_>, body: &'tcx Expr<'_>) -> bool {
- match *pat {
- PatKind::Wild => true,
- PatKind::Binding(_, id, ident, None) if ident.as_str().starts_with('_') => !is_local_used(cx, body, id),
- _ => false,
- }
-}
diff --git a/src/tools/clippy/clippy_lints/src/loops/manual_find.rs b/src/tools/clippy/clippy_lints/src/loops/manual_find.rs
index 0aaa66e6b..a9a9058c9 100644
--- a/src/tools/clippy/clippy_lints/src/loops/manual_find.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/manual_find.rs
@@ -10,7 +10,7 @@ use rustc_hir::def::Res;
use rustc_hir::lang_items::LangItem;
use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, HirId, Node, Pat, PatKind, Stmt, StmtKind};
use rustc_lint::LateContext;
-use rustc_span::source_map::Span;
+use rustc_span::Span;
pub(super) fn check<'tcx>(
cx: &LateContext<'tcx>,
diff --git a/src/tools/clippy/clippy_lints/src/loops/manual_flatten.rs b/src/tools/clippy/clippy_lints/src/loops/manual_flatten.rs
index 559a2c03f..124a35f8f 100644
--- a/src/tools/clippy/clippy_lints/src/loops/manual_flatten.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/manual_flatten.rs
@@ -9,7 +9,7 @@ use rustc_hir::def::{DefKind, Res};
use rustc_hir::{Expr, Pat, PatKind};
use rustc_lint::LateContext;
use rustc_middle::ty;
-use rustc_span::source_map::Span;
+use rustc_span::Span;
/// Check for unnecessary `if let` usage in a for loop where only the `Some` or `Ok` variant of the
/// iterator element is used.
diff --git a/src/tools/clippy/clippy_lints/src/loops/mod.rs b/src/tools/clippy/clippy_lints/src/loops/mod.rs
index 1fb16adad..67c80fb83 100644
--- a/src/tools/clippy/clippy_lints/src/loops/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/mod.rs
@@ -14,17 +14,18 @@ mod needless_range_loop;
mod never_loop;
mod same_item_push;
mod single_element_loop;
+mod unused_enumerate_index;
mod utils;
mod while_immutable_condition;
mod while_let_loop;
mod while_let_on_iterator;
+use clippy_config::msrvs::Msrv;
use clippy_utils::higher;
-use clippy_utils::msrvs::Msrv;
use rustc_hir::{Expr, ExprKind, LoopSource, Pat};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_tool_lint, impl_lint_pass};
-use rustc_span::source_map::Span;
+use rustc_span::Span;
use utils::{make_iterator_snippet, IncrementVisitor, InitializeVisitor};
declare_clippy_lint! {
@@ -36,7 +37,7 @@ declare_clippy_lint! {
/// It is not as fast as a memcpy.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # let src = vec![1];
/// # let mut dst = vec![0; 65];
/// for i in 0..src.len() {
@@ -45,7 +46,7 @@ declare_clippy_lint! {
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # let src = vec![1];
/// # let mut dst = vec![0; 65];
/// dst[64..(src.len() + 64)].clone_from_slice(&src[..]);
@@ -67,7 +68,7 @@ declare_clippy_lint! {
/// the bounds check that is done when indexing.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let vec = vec!['a', 'b', 'c'];
/// for i in 0..vec.len() {
/// println!("{}", vec[i]);
@@ -75,7 +76,7 @@ declare_clippy_lint! {
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// let vec = vec!['a', 'b', 'c'];
/// for i in vec {
/// println!("{}", i);
@@ -100,7 +101,7 @@ declare_clippy_lint! {
/// types.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// // with `y` a `Vec` or slice:
/// # let y = vec![1];
/// for x in y.iter() {
@@ -109,7 +110,7 @@ declare_clippy_lint! {
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # let y = vec![1];
/// for x in &y {
/// // ..
@@ -130,7 +131,7 @@ declare_clippy_lint! {
/// Readability.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # let y = vec![1];
/// // with `y` a `Vec` or slice:
/// for x in y.into_iter() {
@@ -138,7 +139,7 @@ declare_clippy_lint! {
/// }
/// ```
/// can be rewritten to
- /// ```rust
+ /// ```no_run
/// # let y = vec![1];
/// for x in y {
/// // ..
@@ -217,7 +218,7 @@ declare_clippy_lint! {
/// declutters the code and may be faster in some instances.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # let v = vec![1];
/// # fn bar(bar: usize, baz: usize) {}
/// let mut i = 0;
@@ -228,7 +229,7 @@ declare_clippy_lint! {
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # let v = vec![1];
/// # fn bar(bar: usize, baz: usize) {}
/// for (i, item) in v.iter().enumerate() { bar(i, *item); }
@@ -339,7 +340,7 @@ declare_clippy_lint! {
/// code.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// loop {
/// ..;
/// break;
@@ -362,7 +363,7 @@ declare_clippy_lint! {
/// False positive when mutation is followed by a `break`, but the `break` is not immediately
/// after the mutation:
///
- /// ```rust
+ /// ```no_run
/// let mut x = 5;
/// for _ in 0..x {
/// x += 1; // x is a range bound that is mutated
@@ -374,7 +375,7 @@ declare_clippy_lint! {
/// False positive on nested loops ([#6072](https://github.com/rust-lang/rust-clippy/issues/6072))
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let mut foo = 42;
/// for i in 0..foo {
/// foo -= 1;
@@ -402,7 +403,7 @@ declare_clippy_lint! {
/// in the condition and only `Upvar` `b` gets mutated in the body, the lint will not trigger.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let i = 0;
/// while i > 10 {
/// println!("let me loop forever!");
@@ -425,7 +426,7 @@ declare_clippy_lint! {
/// have better performance.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let item1 = 2;
/// let item2 = 3;
/// let mut vec: Vec<u8> = Vec::new();
@@ -438,7 +439,7 @@ declare_clippy_lint! {
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// let item1 = 2;
/// let item2 = 3;
/// let mut vec: Vec<u8> = vec![item1; 20];
@@ -459,7 +460,7 @@ declare_clippy_lint! {
/// single element.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let item1 = 2;
/// for item in &[item1] {
/// println!("{}", item);
@@ -467,7 +468,7 @@ declare_clippy_lint! {
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// let item1 = 2;
/// let item = &item1;
/// println!("{}", item);
@@ -489,7 +490,7 @@ declare_clippy_lint! {
///
/// ### Example
///
- /// ```rust
+ /// ```no_run
/// let x = vec![Some(1), Some(2), Some(3)];
/// for n in x {
/// if let Some(n) = n {
@@ -498,7 +499,7 @@ declare_clippy_lint! {
/// }
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// let x = vec![Some(1), Some(2), Some(3)];
/// for n in x.into_iter().flatten() {
/// println!("{}", n);
@@ -555,7 +556,7 @@ declare_clippy_lint! {
///
/// ### Example
///
- /// ```rust
+ /// ```no_run
/// fn example(arr: Vec<i32>) -> Option<i32> {
/// for el in arr {
/// if el == 1 {
@@ -566,7 +567,7 @@ declare_clippy_lint! {
/// }
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// fn example(arr: Vec<i32>) -> Option<i32> {
/// arr.into_iter().find(|&el| el == 1)
/// }
@@ -579,6 +580,33 @@ declare_clippy_lint! {
declare_clippy_lint! {
/// ### What it does
+ /// Checks for uses of the `enumerate` method where the index is unused (`_`)
+ ///
+ /// ### Why is this bad?
+ /// The index from `.enumerate()` is immediately dropped.
+ ///
+ /// ### Example
+ /// ```rust
+ /// let v = vec![1, 2, 3, 4];
+ /// for (_, x) in v.iter().enumerate() {
+ /// println!("{x}");
+ /// }
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// let v = vec![1, 2, 3, 4];
+ /// for x in v.iter() {
+ /// println!("{x}");
+ /// }
+ /// ```
+ #[clippy::version = "1.75.0"]
+ pub UNUSED_ENUMERATE_INDEX,
+ style,
+ "using `.enumerate()` and immediately dropping the index"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
/// Looks for loops that check for emptiness of a `Vec` in the condition and pop an element
/// in the body as a separate operation.
///
@@ -587,7 +615,7 @@ declare_clippy_lint! {
/// pattern matching on the return value of `Vec::pop()`.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let mut numbers = vec![1, 2, 3, 4, 5];
/// while !numbers.is_empty() {
/// let number = numbers.pop().unwrap();
@@ -595,7 +623,7 @@ declare_clippy_lint! {
/// }
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// let mut numbers = vec![1, 2, 3, 4, 5];
/// while let Some(number) = numbers.pop() {
/// // use `number`
@@ -619,6 +647,7 @@ impl Loops {
}
}
}
+
impl_lint_pass!(Loops => [
MANUAL_MEMCPY,
MANUAL_FLATTEN,
@@ -638,7 +667,8 @@ impl_lint_pass!(Loops => [
SINGLE_ELEMENT_LOOP,
MISSING_SPIN_LOOP,
MANUAL_FIND,
- MANUAL_WHILE_LET_SOME
+ MANUAL_WHILE_LET_SOME,
+ UNUSED_ENUMERATE_INDEX,
]);
impl<'tcx> LateLintPass<'tcx> for Loops {
@@ -717,6 +747,7 @@ impl Loops {
same_item_push::check(cx, pat, arg, body, expr);
manual_flatten::check(cx, pat, arg, body, span);
manual_find::check(cx, pat, arg, body, span, expr);
+ unused_enumerate_index::check(cx, pat, arg, body);
}
fn check_for_loop_arg(&self, cx: &LateContext<'_>, _: &Pat<'_>, arg: &Expr<'_>) {
diff --git a/src/tools/clippy/clippy_lints/src/loops/mut_range_bound.rs b/src/tools/clippy/clippy_lints/src/loops/mut_range_bound.rs
index b83d148b5..2c12d9582 100644
--- a/src/tools/clippy/clippy_lints/src/loops/mut_range_bound.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/mut_range_bound.rs
@@ -9,7 +9,7 @@ use rustc_infer::infer::TyCtxtInferExt;
use rustc_lint::LateContext;
use rustc_middle::mir::FakeReadCause;
use rustc_middle::ty;
-use rustc_span::source_map::Span;
+use rustc_span::Span;
pub(super) fn check(cx: &LateContext<'_>, arg: &Expr<'_>, body: &Expr<'_>) {
if_chain! {
diff --git a/src/tools/clippy/clippy_lints/src/loops/never_loop.rs b/src/tools/clippy/clippy_lints/src/loops/never_loop.rs
index 3d8a4cd94..cc054cb46 100644
--- a/src/tools/clippy/clippy_lints/src/loops/never_loop.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/never_loop.rs
@@ -271,9 +271,9 @@ fn never_loop_expr<'tcx>(
NeverLoopResult::Normal
}
});
- if let NeverLoopResult::Diverging = result &&
- let Some(macro_call) = root_macro_call_first_node(cx, expr) &&
- let Some(sym::todo_macro) = cx.tcx.get_diagnostic_name(macro_call.def_id)
+ if let NeverLoopResult::Diverging = result
+ && let Some(macro_call) = root_macro_call_first_node(cx, expr)
+ && let Some(sym::todo_macro) = cx.tcx.get_diagnostic_name(macro_call.def_id)
{
// We return MayContinueMainLoop here because we treat `todo!()`
// as potentially containing any code, including a continue of the main loop.
diff --git a/src/tools/clippy/clippy_lints/src/loops/same_item_push.rs b/src/tools/clippy/clippy_lints/src/loops/same_item_push.rs
index f7b3b2358..5fffb27cd 100644
--- a/src/tools/clippy/clippy_lints/src/loops/same_item_push.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/same_item_push.rs
@@ -185,7 +185,7 @@ fn get_vec_push<'tcx>(
if let StmtKind::Semi(semi_stmt) = &stmt.kind;
if let ExprKind::MethodCall(path, self_expr, args, _) = &semi_stmt.kind;
// Figure out the parameters for the method call
- if let Some(pushed_item) = args.get(0);
+ if let Some(pushed_item) = args.first();
// Check that the method being called is push() on a Vec
if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(self_expr), sym::Vec);
if path.ident.name.as_str() == "push";
diff --git a/src/tools/clippy/clippy_lints/src/loops/unused_enumerate_index.rs b/src/tools/clippy/clippy_lints/src/loops/unused_enumerate_index.rs
new file mode 100644
index 000000000..dd7fae79d
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/loops/unused_enumerate_index.rs
@@ -0,0 +1,62 @@
+use super::UNUSED_ENUMERATE_INDEX;
+use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then};
+use clippy_utils::source::snippet;
+use clippy_utils::{pat_is_wild, sugg};
+use rustc_hir::def::DefKind;
+use rustc_hir::{Expr, ExprKind, Pat, PatKind};
+use rustc_lint::LateContext;
+use rustc_middle::ty;
+
+/// Checks for the `UNUSED_ENUMERATE_INDEX` lint.
+pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, arg: &'tcx Expr<'_>, body: &'tcx Expr<'_>) {
+ let PatKind::Tuple([index, elem], _) = pat.kind else {
+ return;
+ };
+
+ let ExprKind::MethodCall(_method, self_arg, [], _) = arg.kind else {
+ return;
+ };
+
+ let ty = cx.typeck_results().expr_ty(arg);
+
+ if !pat_is_wild(cx, &index.kind, body) {
+ return;
+ }
+
+ let name = match *ty.kind() {
+ ty::Adt(base, _substs) => cx.tcx.def_path_str(base.did()),
+ _ => return,
+ };
+
+ if name != "std::iter::Enumerate" && name != "core::iter::Enumerate" {
+ return;
+ }
+
+ let Some((DefKind::AssocFn, call_id)) = cx.typeck_results().type_dependent_def(arg.hir_id) else {
+ return;
+ };
+
+ let call_name = cx.tcx.def_path_str(call_id);
+
+ if call_name != "std::iter::Iterator::enumerate" && call_name != "core::iter::Iterator::enumerate" {
+ return;
+ }
+
+ span_lint_and_then(
+ cx,
+ UNUSED_ENUMERATE_INDEX,
+ arg.span,
+ "you seem to use `.enumerate()` and immediately discard the index",
+ |diag| {
+ let base_iter = sugg::Sugg::hir(cx, self_arg, "base iter");
+ multispan_sugg(
+ diag,
+ "remove the `.enumerate()` call",
+ vec![
+ (pat.span, snippet(cx, elem.span, "..").into_owned()),
+ (arg.span, base_iter.to_string()),
+ ],
+ );
+ },
+ );
+}
diff --git a/src/tools/clippy/clippy_lints/src/manual_assert.rs b/src/tools/clippy/clippy_lints/src/manual_assert.rs
index 45ea5aab4..9a3da975f 100644
--- a/src/tools/clippy/clippy_lints/src/manual_assert.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_assert.rs
@@ -16,14 +16,14 @@ declare_clippy_lint! {
/// `assert!` is simpler than `if`-then-`panic!`.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let sad_people: Vec<&str> = vec![];
/// if !sad_people.is_empty() {
/// panic!("there are sad people: {:?}", sad_people);
/// }
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// let sad_people: Vec<&str> = vec![];
/// assert!(sad_people.is_empty(), "there are sad people: {:?}", sad_people);
/// ```
@@ -64,7 +64,8 @@ impl<'tcx> LateLintPass<'tcx> for ManualAssert {
};
let cond_sugg = sugg::Sugg::hir_with_applicability(cx, cond, "..", &mut applicability).maybe_par();
let sugg = format!("assert!({not}{cond_sugg}, {format_args_snip});");
- // we show to the user the suggestion without the comments, but when applying the fix, include the comments in the block
+ // we show to the user the suggestion without the comments, but when applying the fix, include the
+ // comments in the block
span_lint_and_then(
cx,
MANUAL_ASSERT,
@@ -77,16 +78,11 @@ impl<'tcx> LateLintPass<'tcx> for ManualAssert {
expr.span.shrink_to_lo(),
"add comments back",
comments,
- applicability
+ applicability,
);
}
- diag.span_suggestion(
- expr.span,
- "try instead",
- sugg,
- applicability
- );
- }
+ diag.span_suggestion(expr.span, "try instead", sugg, applicability);
+ },
);
}
}
diff --git a/src/tools/clippy/clippy_lints/src/manual_async_fn.rs b/src/tools/clippy/clippy_lints/src/manual_async_fn.rs
index 577bc1d66..998de38a9 100644
--- a/src/tools/clippy/clippy_lints/src/manual_async_fn.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_async_fn.rs
@@ -4,7 +4,7 @@ use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::intravisit::FnKind;
use rustc_hir::{
- AsyncGeneratorKind, Block, Body, Closure, Expr, ExprKind, FnDecl, FnRetTy, GeneratorKind, GenericArg, GenericBound,
+ Block, Body, Closure, CoroutineKind, CoroutineSource, Expr, ExprKind, FnDecl, FnRetTy, GenericArg, GenericBound,
ImplItem, Item, ItemKind, LifetimeName, Node, Term, TraitRef, Ty, TyKind, TypeBindingKind,
};
use rustc_lint::{LateContext, LateLintPass};
@@ -20,13 +20,13 @@ declare_clippy_lint! {
/// It's more idiomatic to use the dedicated syntax.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// use std::future::Future;
///
/// fn foo() -> impl Future<Output = i32> { async { 42 } }
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// async fn foo() -> i32 { 42 }
/// ```
#[clippy::version = "1.45.0"]
@@ -188,7 +188,7 @@ fn desugared_async_block<'tcx>(cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>)
..
} = block_expr;
let closure_body = cx.tcx.hir().body(body);
- if closure_body.generator_kind == Some(GeneratorKind::Async(AsyncGeneratorKind::Block));
+ if closure_body.coroutine_kind == Some(CoroutineKind::Async(CoroutineSource::Block));
then {
return Some(closure_body);
}
diff --git a/src/tools/clippy/clippy_lints/src/manual_bits.rs b/src/tools/clippy/clippy_lints/src/manual_bits.rs
index 6c7c57ba1..cd614c895 100644
--- a/src/tools/clippy/clippy_lints/src/manual_bits.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_bits.rs
@@ -1,6 +1,6 @@
+use clippy_config::msrvs::{self, Msrv};
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::get_parent_expr;
-use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::source::snippet_with_context;
use rustc_ast::ast::LitKind;
use rustc_errors::Applicability;
@@ -20,11 +20,11 @@ declare_clippy_lint! {
/// Can be written as the shorter `T::BITS`.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// std::mem::size_of::<usize>() * 8;
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// usize::BITS as usize;
/// ```
#[clippy::version = "1.60.0"]
@@ -103,9 +103,9 @@ fn get_size_of_ty<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<
if let ExprKind::Path(ref count_func_qpath) = count_func.kind;
if let QPath::Resolved(_, count_func_path) = count_func_qpath;
- if let Some(segment_zero) = count_func_path.segments.get(0);
+ if let Some(segment_zero) = count_func_path.segments.first();
if let Some(args) = segment_zero.args;
- if let Some(GenericArg::Type(real_ty)) = args.args.get(0);
+ if let Some(GenericArg::Type(real_ty)) = args.args.first();
if let Some(def_id) = cx.qpath_res(count_func_qpath, count_func.hir_id).opt_def_id();
if cx.tcx.is_diagnostic_item(sym::mem_size_of, def_id);
diff --git a/src/tools/clippy/clippy_lints/src/manual_clamp.rs b/src/tools/clippy/clippy_lints/src/manual_clamp.rs
index e75666e61..09c90e38e 100644
--- a/src/tools/clippy/clippy_lints/src/manual_clamp.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_clamp.rs
@@ -1,6 +1,6 @@
+use clippy_config::msrvs::{self, Msrv};
use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then};
use clippy_utils::higher::If;
-use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::sugg::Sugg;
use clippy_utils::ty::implements_trait;
use clippy_utils::visitors::is_const_evaluatable;
@@ -38,7 +38,7 @@ declare_clippy_lint! {
/// PR](https://github.com/rust-lang/rust-clippy/pull/9484#issuecomment-1278922613).
///
/// ### Examples
- /// ```rust
+ /// ```no_run
/// # let (input, min, max) = (0, -2, 1);
/// if input > max {
/// max
@@ -50,13 +50,13 @@ declare_clippy_lint! {
/// # ;
/// ```
///
- /// ```rust
+ /// ```no_run
/// # let (input, min, max) = (0, -2, 1);
/// input.max(min).min(max)
/// # ;
/// ```
///
- /// ```rust
+ /// ```no_run
/// # let (input, min, max) = (0, -2, 1);
/// match input {
/// x if x > max => max,
@@ -66,14 +66,14 @@ declare_clippy_lint! {
/// # ;
/// ```
///
- /// ```rust
+ /// ```no_run
/// # let (input, min, max) = (0, -2, 1);
/// let mut x = input;
/// if x < min { x = min; }
/// if x > max { x = max; }
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # let (input, min, max) = (0, -2, 1);
/// input.clamp(min, max)
/// # ;
@@ -207,7 +207,7 @@ impl TypeClampability {
/// Targets patterns like
///
-/// ```
+/// ```no_run
/// # let (input, min, max) = (0, -3, 12);
///
/// if input < min {
@@ -225,11 +225,11 @@ fn is_if_elseif_else_pattern<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx
then,
r#else: Some(else_if),
}) = If::hir(expr)
- && let Some(If {
- cond: else_if_cond,
- then: else_if_then,
- r#else: Some(else_body),
- }) = If::hir(peel_blocks(else_if))
+ && let Some(If {
+ cond: else_if_cond,
+ then: else_if_then,
+ r#else: Some(else_body),
+ }) = If::hir(peel_blocks(else_if))
{
let params = is_clamp_meta_pattern(
cx,
@@ -256,7 +256,7 @@ fn is_if_elseif_else_pattern<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx
/// Targets patterns like
///
-/// ```
+/// ```no_run
/// # let (input, min_value, max_value) = (0, -3, 12);
///
/// input.max(min_value).min(max_value)
@@ -275,7 +275,12 @@ fn is_max_min_pattern<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> O
_ => return None,
};
Some(ClampSuggestion {
- params: InputMinMax { input, min, max, is_float },
+ params: InputMinMax {
+ input,
+ min,
+ max,
+ is_float,
+ },
span: expr.span,
make_assignment: None,
hir_with_ignore_attr: None,
@@ -287,7 +292,7 @@ fn is_max_min_pattern<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> O
/// Targets patterns like
///
-/// ```
+/// ```no_run
/// # let (input, min_value, max_value) = (0, -3, 12);
/// # use std::cmp::{max, min};
/// min(max(input, min_value), max_value)
@@ -346,11 +351,16 @@ fn is_call_max_min_pattern<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>)
("max", "min") => (inner_arg, outer_arg),
_ => return None,
}
- }
+ },
_ => return None,
};
Some(ClampSuggestion {
- params: InputMinMax { input, min, max, is_float },
+ params: InputMinMax {
+ input,
+ min,
+ max,
+ is_float,
+ },
span,
make_assignment: None,
hir_with_ignore_attr: None,
@@ -369,7 +379,7 @@ fn is_call_max_min_pattern<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>)
/// Targets patterns like
///
-/// ```
+/// ```no_run
/// # let (input, min, max) = (0, -3, 12);
///
/// match input {
@@ -384,7 +394,8 @@ fn is_match_pattern<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Opt
// Find possible min/max branches
let minmax_values = |a: &'tcx Arm<'tcx>| {
if let PatKind::Binding(_, var_hir_id, _, None) = &a.pat.kind
- && let Some(Guard::If(e)) = a.guard {
+ && let Some(Guard::If(e)) = a.guard
+ {
Some((e, var_hir_id, a.body))
} else {
None
@@ -428,7 +439,7 @@ fn is_match_pattern<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Opt
/// Targets patterns like
///
-/// ```
+/// ```no_run
/// # let (input, min, max) = (0, -3, 12);
///
/// let mut x = input;
@@ -441,18 +452,20 @@ fn is_two_if_pattern<'tcx>(cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) ->
.filter_map(|(maybe_set_first, maybe_set_second)| {
if let StmtKind::Expr(first_expr) = *maybe_set_first
&& let StmtKind::Expr(second_expr) = *maybe_set_second
- && let Some(If { cond: first_cond, then: first_then, r#else: None }) = If::hir(first_expr)
- && let Some(If { cond: second_cond, then: second_then, r#else: None }) = If::hir(second_expr)
- && let ExprKind::Assign(
- maybe_input_first_path,
- maybe_min_max_first,
- _
- ) = peel_blocks_with_stmt(first_then).kind
- && let ExprKind::Assign(
- maybe_input_second_path,
- maybe_min_max_second,
- _
- ) = peel_blocks_with_stmt(second_then).kind
+ && let Some(If {
+ cond: first_cond,
+ then: first_then,
+ r#else: None,
+ }) = If::hir(first_expr)
+ && let Some(If {
+ cond: second_cond,
+ then: second_then,
+ r#else: None,
+ }) = If::hir(second_expr)
+ && let ExprKind::Assign(maybe_input_first_path, maybe_min_max_first, _) =
+ peel_blocks_with_stmt(first_then).kind
+ && let ExprKind::Assign(maybe_input_second_path, maybe_min_max_second, _) =
+ peel_blocks_with_stmt(second_then).kind
&& eq_expr_value(cx, maybe_input_first_path, maybe_input_second_path)
&& let Some(first_bin) = BinaryOp::new(first_cond)
&& let Some(second_bin) = BinaryOp::new(second_cond)
@@ -462,7 +475,7 @@ fn is_two_if_pattern<'tcx>(cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) ->
&second_bin,
maybe_min_max_first,
maybe_min_max_second,
- None
+ None,
)
{
Some(ClampSuggestion {
@@ -485,7 +498,7 @@ fn is_two_if_pattern<'tcx>(cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) ->
/// Targets patterns like
///
-/// ```
+/// ```no_run
/// # let (mut input, min, max) = (0, -3, 12);
///
/// if input < min {
@@ -505,16 +518,9 @@ fn is_if_elseif_pattern<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) ->
then: else_if_then,
r#else: None,
}) = If::hir(peel_blocks(else_if))
- && let ExprKind::Assign(
- maybe_input_first_path,
- maybe_min_max_first,
- _
- ) = peel_blocks_with_stmt(then).kind
- && let ExprKind::Assign(
- maybe_input_second_path,
- maybe_min_max_second,
- _
- ) = peel_blocks_with_stmt(else_if_then).kind
+ && let ExprKind::Assign(maybe_input_first_path, maybe_min_max_first, _) = peel_blocks_with_stmt(then).kind
+ && let ExprKind::Assign(maybe_input_second_path, maybe_min_max_second, _) =
+ peel_blocks_with_stmt(else_if_then).kind
{
let params = is_clamp_meta_pattern(
cx,
diff --git a/src/tools/clippy/clippy_lints/src/manual_float_methods.rs b/src/tools/clippy/clippy_lints/src/manual_float_methods.rs
index 88db7ae6a..f923e0ac8 100644
--- a/src/tools/clippy/clippy_lints/src/manual_float_methods.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_float_methods.rs
@@ -17,16 +17,16 @@ declare_clippy_lint! {
/// The method `is_infinite` is shorter and more readable.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # let x = 1.0f32;
/// if x == f32::INFINITY || x == f32::NEG_INFINITY {}
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # let x = 1.0f32;
/// if x.is_infinite() {}
/// ```
- #[clippy::version = "1.72.0"]
+ #[clippy::version = "1.73.0"]
pub MANUAL_IS_INFINITE,
style,
"use dedicated method to check if a float is infinite"
@@ -40,18 +40,18 @@ declare_clippy_lint! {
/// The method `is_finite` is shorter and more readable.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # let x = 1.0f32;
/// if x != f32::INFINITY && x != f32::NEG_INFINITY {}
/// if x.abs() < f32::INFINITY {}
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # let x = 1.0f32;
/// if x.is_finite() {}
/// if x.is_finite() {}
/// ```
- #[clippy::version = "1.72.0"]
+ #[clippy::version = "1.73.0"]
pub MANUAL_IS_FINITE,
style,
"use dedicated method to check if a float is finite"
@@ -85,7 +85,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualFloatMethods {
if !in_external_macro(cx.sess(), expr.span)
&& (
matches!(cx.tcx.constness(cx.tcx.hir().enclosing_body_owner(expr.hir_id)), Constness::NotConst)
- || cx.tcx.features().active(sym!(const_float_classify))
+ || cx.tcx.features().declared(sym!(const_float_classify))
) && let ExprKind::Binary(kind, lhs, rhs) = expr.kind
&& let ExprKind::Binary(lhs_kind, lhs_lhs, lhs_rhs) = lhs.kind
&& let ExprKind::Binary(rhs_kind, rhs_lhs, rhs_rhs) = rhs.kind
@@ -105,7 +105,6 @@ impl<'tcx> LateLintPass<'tcx> for ManualFloatMethods {
// case somebody does that for some reason
&& (is_infinity(const_1) && is_neg_infinity(const_2)
|| is_neg_infinity(const_1) && is_infinity(const_2))
- && !is_from_proc_macro(cx, expr)
&& let Some(local_snippet) = snippet_opt(cx, first.span)
{
let variant = match (kind.node, lhs_kind.node, rhs_kind.node) {
@@ -113,47 +112,44 @@ impl<'tcx> LateLintPass<'tcx> for ManualFloatMethods {
(BinOpKind::And, BinOpKind::Ne, BinOpKind::Ne) => Variant::ManualIsFinite,
_ => return,
};
+ if is_from_proc_macro(cx, expr) {
+ return;
+ }
- span_lint_and_then(
- cx,
- variant.lint(),
- expr.span,
- variant.msg(),
- |diag| {
- match variant {
- Variant::ManualIsInfinite => {
- diag.span_suggestion(
- expr.span,
- "use the dedicated method instead",
- format!("{local_snippet}.is_infinite()"),
- Applicability::MachineApplicable,
- );
- },
- Variant::ManualIsFinite => {
- // TODO: There's probably some better way to do this, i.e., create
- // multiple suggestions with notes between each of them
- diag.span_suggestion_verbose(
- expr.span,
- "use the dedicated method instead",
- format!("{local_snippet}.is_finite()"),
- Applicability::MaybeIncorrect,
- )
- .span_suggestion_verbose(
- expr.span,
- "this will alter how it handles NaN; if that is a problem, use instead",
- format!("{local_snippet}.is_finite() || {local_snippet}.is_nan()"),
- Applicability::MaybeIncorrect,
- )
- .span_suggestion_verbose(
- expr.span,
- "or, for conciseness",
- format!("!{local_snippet}.is_infinite()"),
- Applicability::MaybeIncorrect,
- );
- },
- }
- },
- );
+ span_lint_and_then(cx, variant.lint(), expr.span, variant.msg(), |diag| {
+ match variant {
+ Variant::ManualIsInfinite => {
+ diag.span_suggestion(
+ expr.span,
+ "use the dedicated method instead",
+ format!("{local_snippet}.is_infinite()"),
+ Applicability::MachineApplicable,
+ );
+ },
+ Variant::ManualIsFinite => {
+ // TODO: There's probably some better way to do this, i.e., create
+ // multiple suggestions with notes between each of them
+ diag.span_suggestion_verbose(
+ expr.span,
+ "use the dedicated method instead",
+ format!("{local_snippet}.is_finite()"),
+ Applicability::MaybeIncorrect,
+ )
+ .span_suggestion_verbose(
+ expr.span,
+ "this will alter how it handles NaN; if that is a problem, use instead",
+ format!("{local_snippet}.is_finite() || {local_snippet}.is_nan()"),
+ Applicability::MaybeIncorrect,
+ )
+ .span_suggestion_verbose(
+ expr.span,
+ "or, for conciseness",
+ format!("!{local_snippet}.is_infinite()"),
+ Applicability::MaybeIncorrect,
+ );
+ },
+ }
+ });
}
}
}
diff --git a/src/tools/clippy/clippy_lints/src/manual_hash_one.rs b/src/tools/clippy/clippy_lints/src/manual_hash_one.rs
new file mode 100644
index 000000000..472b4eb90
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/manual_hash_one.rs
@@ -0,0 +1,132 @@
+use clippy_config::msrvs::{self, Msrv};
+use clippy_utils::diagnostics::span_lint_hir_and_then;
+use clippy_utils::source::snippet_opt;
+use clippy_utils::visitors::{is_local_used, local_used_once};
+use clippy_utils::{is_trait_method, path_to_local_id};
+use rustc_errors::Applicability;
+use rustc_hir::{BindingAnnotation, ExprKind, Local, Node, PatKind, StmtKind};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_tool_lint, impl_lint_pass};
+use rustc_span::sym;
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for cases where [`BuildHasher::hash_one`] can be used.
+ ///
+ /// [`BuildHasher::hash_one`]: https://doc.rust-lang.org/std/hash/trait.BuildHasher.html#method.hash_one
+ ///
+ /// ### Why is this bad?
+ /// It is more concise to use the `hash_one` method.
+ ///
+ /// ### Example
+ /// ```no_run
+ /// use std::hash::{BuildHasher, Hash, Hasher};
+ /// use std::collections::hash_map::RandomState;
+ ///
+ /// let s = RandomState::new();
+ /// let value = vec![1, 2, 3];
+ ///
+ /// let mut hasher = s.build_hasher();
+ /// value.hash(&mut hasher);
+ /// let hash = hasher.finish();
+ /// ```
+ /// Use instead:
+ /// ```no_run
+ /// use std::hash::BuildHasher;
+ /// use std::collections::hash_map::RandomState;
+ ///
+ /// let s = RandomState::new();
+ /// let value = vec![1, 2, 3];
+ ///
+ /// let hash = s.hash_one(&value);
+ /// ```
+ #[clippy::version = "1.74.0"]
+ pub MANUAL_HASH_ONE,
+ complexity,
+ "manual implementations of `BuildHasher::hash_one`"
+}
+
+pub struct ManualHashOne {
+ msrv: Msrv,
+}
+
+impl ManualHashOne {
+ #[must_use]
+ pub fn new(msrv: Msrv) -> Self {
+ Self { msrv }
+ }
+}
+
+impl_lint_pass!(ManualHashOne => [MANUAL_HASH_ONE]);
+
+impl LateLintPass<'_> for ManualHashOne {
+ fn check_local(&mut self, cx: &LateContext<'_>, local: &Local<'_>) {
+ // `let mut hasher = seg.build_hasher();`
+ if let PatKind::Binding(BindingAnnotation::MUT, hasher, _, None) = local.pat.kind
+ && let Some(init) = local.init
+ && !init.span.from_expansion()
+ && let ExprKind::MethodCall(seg, build_hasher, [], _) = init.kind
+ && seg.ident.name == sym!(build_hasher)
+
+ && let Node::Stmt(local_stmt) = cx.tcx.hir().get_parent(local.hir_id)
+ && let Node::Block(block) = cx.tcx.hir().get_parent(local_stmt.hir_id)
+
+ && let mut stmts = block.stmts.iter()
+ .skip_while(|stmt| stmt.hir_id != local_stmt.hir_id)
+ .skip(1)
+ .filter(|&stmt| is_local_used(cx, stmt, hasher))
+
+ // `hashed_value.hash(&mut hasher);`
+ && let Some(hash_stmt) = stmts.next()
+ && let StmtKind::Semi(hash_expr) = hash_stmt.kind
+ && !hash_expr.span.from_expansion()
+ && let ExprKind::MethodCall(seg, hashed_value, [ref_to_hasher], _) = hash_expr.kind
+ && seg.ident.name == sym::hash
+ && is_trait_method(cx, hash_expr, sym::Hash)
+ && path_to_local_id(ref_to_hasher.peel_borrows(), hasher)
+
+ && let maybe_finish_stmt = stmts.next()
+ // There should be no more statements referencing `hasher`
+ && stmts.next().is_none()
+
+ // `hasher.finish()`, may be anywhere in a statement or the trailing expr of the block
+ && let Some(path_expr) = local_used_once(cx, (maybe_finish_stmt, block.expr), hasher)
+ && let Node::Expr(finish_expr) = cx.tcx.hir().get_parent(path_expr.hir_id)
+ && !finish_expr.span.from_expansion()
+ && let ExprKind::MethodCall(seg, _, [], _) = finish_expr.kind
+ && seg.ident.name == sym!(finish)
+
+ && self.msrv.meets(msrvs::BUILD_HASHER_HASH_ONE)
+ {
+ span_lint_hir_and_then(
+ cx,
+ MANUAL_HASH_ONE,
+ finish_expr.hir_id,
+ finish_expr.span,
+ "manual implementation of `BuildHasher::hash_one`",
+ |diag| {
+ if let Some(build_hasher) = snippet_opt(cx, build_hasher.span)
+ && let Some(hashed_value) = snippet_opt(cx, hashed_value.span)
+ {
+ diag.multipart_suggestion(
+ "try",
+ vec![
+ (local_stmt.span, String::new()),
+ (hash_stmt.span, String::new()),
+ (
+ finish_expr.span,
+ // `needless_borrows_for_generic_args` will take care of
+ // removing the `&` when it isn't needed
+ format!("{build_hasher}.hash_one(&{hashed_value})"),
+ ),
+ ],
+ Applicability::MachineApplicable,
+ );
+ }
+ },
+ );
+ }
+ }
+
+ extract_msrv_attr!(LateContext);
+}
diff --git a/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs b/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs
index f26442447..468f41707 100644
--- a/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs
@@ -1,6 +1,6 @@
+use clippy_config::msrvs::{self, Msrv};
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::macros::root_macro_call;
-use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::sugg::Sugg;
use clippy_utils::{higher, in_constant};
use rustc_ast::ast::RangeLimits;
@@ -15,19 +15,21 @@ use rustc_span::{sym, Span};
declare_clippy_lint! {
/// ### What it does
/// Suggests to use dedicated built-in methods,
- /// `is_ascii_(lowercase|uppercase|digit)` for checking on corresponding ascii range
+ /// `is_ascii_(lowercase|uppercase|digit|hexdigit)` for checking on corresponding
+ /// ascii range
///
/// ### Why is this bad?
/// Using the built-in functions is more readable and makes it
/// clear that it's not a specific subset of characters, but all
- /// ASCII (lowercase|uppercase|digit) characters.
+ /// ASCII (lowercase|uppercase|digit|hexdigit) characters.
/// ### Example
- /// ```rust
+ /// ```no_run
/// fn main() {
/// assert!(matches!('x', 'a'..='z'));
/// assert!(matches!(b'X', b'A'..=b'Z'));
/// assert!(matches!('2', '0'..='9'));
/// assert!(matches!('x', 'A'..='Z' | 'a'..='z'));
+ /// assert!(matches!('C', '0'..='9' | 'a'..='f' | 'A'..='F'));
///
/// ('0'..='9').contains(&'0');
/// ('a'..='z').contains(&'a');
@@ -35,12 +37,13 @@ declare_clippy_lint! {
/// }
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// fn main() {
/// assert!('x'.is_ascii_lowercase());
/// assert!(b'X'.is_ascii_uppercase());
/// assert!('2'.is_ascii_digit());
/// assert!('x'.is_ascii_alphabetic());
+ /// assert!('C'.is_ascii_hexdigit());
///
/// '0'.is_ascii_digit();
/// 'a'.is_ascii_lowercase();
@@ -75,6 +78,12 @@ enum CharRange {
FullChar,
/// '0..=9'
Digit,
+ /// 'a..=f'
+ LowerHexLetter,
+ /// 'A..=F'
+ UpperHexLetter,
+ /// '0..=9' | 'a..=f' | 'A..=F'
+ HexDigit,
Otherwise,
}
@@ -89,15 +98,20 @@ impl<'tcx> LateLintPass<'tcx> for ManualIsAsciiCheck {
}
if let Some(macro_call) = root_macro_call(expr.span)
- && is_matches_macro(cx, macro_call.def_id) {
+ && is_matches_macro(cx, macro_call.def_id)
+ {
if let ExprKind::Match(recv, [arm, ..], _) = expr.kind {
let range = check_pat(&arm.pat.kind);
check_is_ascii(cx, macro_call.span, recv, &range);
}
} else if let ExprKind::MethodCall(path, receiver, [arg], ..) = expr.kind
&& path.ident.name == sym!(contains)
- && let Some(higher::Range { start: Some(start), end: Some(end), limits: RangeLimits::Closed })
- = higher::Range::hir(receiver) {
+ && let Some(higher::Range {
+ start: Some(start),
+ end: Some(end),
+ limits: RangeLimits::Closed,
+ }) = higher::Range::hir(receiver)
+ {
let range = check_range(start, end);
if let ExprKind::AddrOf(BorrowKind::Ref, _, e) = arg.kind {
check_is_ascii(cx, expr.span, e, &range);
@@ -116,7 +130,8 @@ fn check_is_ascii(cx: &LateContext<'_>, span: Span, recv: &Expr<'_>, range: &Cha
CharRange::LowerChar => Some("is_ascii_lowercase"),
CharRange::FullChar => Some("is_ascii_alphabetic"),
CharRange::Digit => Some("is_ascii_digit"),
- CharRange::Otherwise => None,
+ CharRange::HexDigit => Some("is_ascii_hexdigit"),
+ CharRange::Otherwise | CharRange::LowerHexLetter | CharRange::UpperHexLetter => None,
} {
let default_snip = "..";
let mut app = Applicability::MachineApplicable;
@@ -141,6 +156,12 @@ fn check_pat(pat_kind: &PatKind<'_>) -> CharRange {
if ranges.len() == 2 && ranges.contains(&CharRange::UpperChar) && ranges.contains(&CharRange::LowerChar) {
CharRange::FullChar
+ } else if ranges.len() == 3
+ && ranges.contains(&CharRange::Digit)
+ && ranges.contains(&CharRange::LowerHexLetter)
+ && ranges.contains(&CharRange::UpperHexLetter)
+ {
+ CharRange::HexDigit
} else {
CharRange::Otherwise
}
@@ -152,10 +173,13 @@ fn check_pat(pat_kind: &PatKind<'_>) -> CharRange {
fn check_range(start: &Expr<'_>, end: &Expr<'_>) -> CharRange {
if let ExprKind::Lit(start_lit) = &start.kind
- && let ExprKind::Lit(end_lit) = &end.kind {
+ && let ExprKind::Lit(end_lit) = &end.kind
+ {
match (&start_lit.node, &end_lit.node) {
(Char('a'), Char('z')) | (Byte(b'a'), Byte(b'z')) => CharRange::LowerChar,
(Char('A'), Char('Z')) | (Byte(b'A'), Byte(b'Z')) => CharRange::UpperChar,
+ (Char('a'), Char('f')) | (Byte(b'a'), Byte(b'f')) => CharRange::LowerHexLetter,
+ (Char('A'), Char('F')) | (Byte(b'A'), Byte(b'F')) => CharRange::UpperHexLetter,
(Char('0'), Char('9')) | (Byte(b'0'), Byte(b'9')) => CharRange::Digit,
_ => CharRange::Otherwise,
}
diff --git a/src/tools/clippy/clippy_lints/src/manual_let_else.rs b/src/tools/clippy/clippy_lints/src/manual_let_else.rs
index c531137b7..170a040d4 100644
--- a/src/tools/clippy/clippy_lints/src/manual_let_else.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_let_else.rs
@@ -1,10 +1,12 @@
use crate::question_mark::{QuestionMark, QUESTION_MARK};
+use clippy_config::msrvs;
+use clippy_config::types::MatchLintBehaviour;
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::higher::IfLetOrMatch;
use clippy_utils::source::snippet_with_context;
use clippy_utils::ty::is_type_diagnostic_item;
use clippy_utils::visitors::{Descend, Visitable};
-use clippy_utils::{is_lint_allowed, msrvs, pat_and_expr_can_be_question_mark, peel_blocks};
+use clippy_utils::{is_lint_allowed, pat_and_expr_can_be_question_mark, peel_blocks};
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_errors::Applicability;
use rustc_hir::intravisit::{walk_expr, Visitor};
@@ -14,7 +16,6 @@ use rustc_middle::lint::in_external_macro;
use rustc_session::declare_tool_lint;
use rustc_span::symbol::{sym, Symbol};
use rustc_span::Span;
-use serde::Deserialize;
use std::ops::ControlFlow;
use std::slice;
@@ -30,14 +31,14 @@ declare_clippy_lint! {
///
/// ### Example
///
- /// ```rust
+ /// ```no_run
/// # let w = Some(0);
/// let v = if let Some(v) = w { v } else { return };
/// ```
///
/// Could be written:
///
- /// ```rust
+ /// ```no_run
/// # fn main () {
/// # let w = Some(0);
/// let Some(v) = w else { return };
@@ -55,21 +56,20 @@ impl<'tcx> QuestionMark {
return;
}
- if let StmtKind::Local(local) = stmt.kind &&
- let Some(init) = local.init &&
- local.els.is_none() &&
- local.ty.is_none() &&
- init.span.ctxt() == stmt.span.ctxt() &&
- let Some(if_let_or_match) = IfLetOrMatch::parse(cx, init)
+ if let StmtKind::Local(local) = stmt.kind
+ && let Some(init) = local.init
+ && local.els.is_none()
+ && local.ty.is_none()
+ && init.span.eq_ctxt(stmt.span)
+ && let Some(if_let_or_match) = IfLetOrMatch::parse(cx, init)
{
match if_let_or_match {
IfLetOrMatch::IfLet(if_let_expr, let_pat, if_then, if_else) => {
- if
- let Some(ident_map) = expr_simple_identity_map(local.pat, let_pat, if_then) &&
- let Some(if_else) = if_else &&
- expr_diverges(cx, if_else) &&
- let qm_allowed = is_lint_allowed(cx, QUESTION_MARK, stmt.hir_id) &&
- (qm_allowed || pat_and_expr_can_be_question_mark(cx, let_pat, if_else).is_none())
+ if let Some(ident_map) = expr_simple_identity_map(local.pat, let_pat, if_then)
+ && let Some(if_else) = if_else
+ && expr_diverges(cx, if_else)
+ && let qm_allowed = is_lint_allowed(cx, QUESTION_MARK, stmt.hir_id)
+ && (qm_allowed || pat_and_expr_can_be_question_mark(cx, let_pat, if_else).is_none())
{
emit_manual_let_else(cx, stmt.span, if_let_expr, &ident_map, let_pat, if_else);
}
@@ -95,7 +95,9 @@ impl<'tcx> QuestionMark {
.iter()
.enumerate()
.find(|(_, arm)| expr_diverges(cx, arm.body) && pat_allowed_for_else(cx, arm.pat, check_types));
- let Some((idx, diverging_arm)) = diverging_arm_opt else { return; };
+ let Some((idx, diverging_arm)) = diverging_arm_opt else {
+ return;
+ };
// If the non-diverging arm is the first one, its pattern can be reused in a let/else statement.
// However, if it arrives in second position, its pattern may cover some cases already covered
// by the diverging one.
@@ -105,7 +107,7 @@ impl<'tcx> QuestionMark {
}
let pat_arm = &arms[1 - idx];
let Some(ident_map) = expr_simple_identity_map(local.pat, pat_arm.pat, pat_arm.body) else {
- return
+ return;
};
emit_manual_let_else(cx, stmt.span, match_expr, &ident_map, pat_arm.pat, diverging_arm.body);
@@ -136,9 +138,9 @@ fn emit_manual_let_else(
// for this to be machine applicable.
let mut app = Applicability::HasPlaceholders;
let (sn_expr, _) = snippet_with_context(cx, expr.span, span.ctxt(), "", &mut app);
- let (sn_else, _) = snippet_with_context(cx, else_body.span, span.ctxt(), "", &mut app);
+ let (sn_else, else_is_mac_call) = snippet_with_context(cx, else_body.span, span.ctxt(), "", &mut app);
- let else_bl = if matches!(else_body.kind, ExprKind::Block(..)) {
+ let else_bl = if matches!(else_body.kind, ExprKind::Block(..)) && !else_is_mac_call {
sn_else.into_owned()
} else {
format!("{{ {sn_else} }}")
@@ -216,8 +218,8 @@ fn replace_in_pattern(
let fields = fields
.iter()
.map(|fld| {
- if let PatKind::Binding(_, _, name, None) = fld.pat.kind &&
- let Some(pat_to_put) = ident_map.get(&name.name)
+ if let PatKind::Binding(_, _, name, None) = fld.pat.kind
+ && let Some(pat_to_put) = ident_map.get(&name.name)
{
let (sn_fld_name, _) = snippet_with_context(cx, fld.ident.span, span.ctxt(), "", app);
let (sn_ptp, _) = snippet_with_context(cx, pat_to_put.span, span.ctxt(), "", app);
@@ -463,8 +465,8 @@ fn expr_simple_identity_map<'a, 'hir>(
}
let mut ident_map = FxHashMap::default();
for (sub_pat, path) in sub_pats.iter().zip(paths.iter()) {
- if let ExprKind::Path(QPath::Resolved(_ty, path)) = path.kind &&
- let [path_seg] = path.segments
+ if let ExprKind::Path(QPath::Resolved(_ty, path)) = path.kind
+ && let [path_seg] = path.segments
{
let ident = path_seg.ident;
if !pat_bindings.remove(&ident) {
@@ -477,10 +479,3 @@ fn expr_simple_identity_map<'a, 'hir>(
}
Some(ident_map)
}
-
-#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Deserialize)]
-pub enum MatchLintBehaviour {
- AllTypes,
- WellKnownTypes,
- Never,
-}
diff --git a/src/tools/clippy/clippy_lints/src/manual_main_separator_str.rs b/src/tools/clippy/clippy_lints/src/manual_main_separator_str.rs
index c292bbe4e..23f47c86f 100644
--- a/src/tools/clippy/clippy_lints/src/manual_main_separator_str.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_main_separator_str.rs
@@ -1,5 +1,5 @@
+use clippy_config::msrvs::{self, Msrv};
use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::{is_trait_method, match_def_path, paths, peel_hir_expr_refs};
use rustc_errors::Applicability;
use rustc_hir::def::{DefKind, Res};
@@ -19,11 +19,11 @@ declare_clippy_lint! {
/// an extra memory allocation.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let s: &str = &std::path::MAIN_SEPARATOR.to_string();
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// let s: &str = std::path::MAIN_SEPARATOR_STR;
/// ```
#[clippy::version = "1.70.0"]
@@ -47,27 +47,27 @@ impl_lint_pass!(ManualMainSeparatorStr => [MANUAL_MAIN_SEPARATOR_STR]);
impl LateLintPass<'_> for ManualMainSeparatorStr {
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
- if self.msrv.meets(msrvs::PATH_MAIN_SEPARATOR_STR) &&
- let (target, _) = peel_hir_expr_refs(expr) &&
- is_trait_method(cx, target, sym::ToString) &&
- let ExprKind::MethodCall(path, receiver, &[], _) = target.kind &&
- path.ident.name == sym::to_string &&
- let ExprKind::Path(QPath::Resolved(None, path)) = receiver.kind &&
- let Res::Def(DefKind::Const, receiver_def_id) = path.res &&
- match_def_path(cx, receiver_def_id, &paths::PATH_MAIN_SEPARATOR) &&
- let ty::Ref(_, ty, Mutability::Not) = cx.typeck_results().expr_ty_adjusted(expr).kind() &&
- ty.is_str()
- {
- span_lint_and_sugg(
- cx,
- MANUAL_MAIN_SEPARATOR_STR,
- expr.span,
- "taking a reference on `std::path::MAIN_SEPARATOR` conversion to `String`",
- "replace with",
- "std::path::MAIN_SEPARATOR_STR".to_owned(),
- Applicability::MachineApplicable,
- );
- }
+ if self.msrv.meets(msrvs::PATH_MAIN_SEPARATOR_STR)
+ && let (target, _) = peel_hir_expr_refs(expr)
+ && is_trait_method(cx, target, sym::ToString)
+ && let ExprKind::MethodCall(path, receiver, &[], _) = target.kind
+ && path.ident.name == sym::to_string
+ && let ExprKind::Path(QPath::Resolved(None, path)) = receiver.kind
+ && let Res::Def(DefKind::Const, receiver_def_id) = path.res
+ && match_def_path(cx, receiver_def_id, &paths::PATH_MAIN_SEPARATOR)
+ && let ty::Ref(_, ty, Mutability::Not) = cx.typeck_results().expr_ty_adjusted(expr).kind()
+ && ty.is_str()
+ {
+ span_lint_and_sugg(
+ cx,
+ MANUAL_MAIN_SEPARATOR_STR,
+ expr.span,
+ "taking a reference on `std::path::MAIN_SEPARATOR` conversion to `String`",
+ "replace with",
+ "std::path::MAIN_SEPARATOR_STR".to_owned(),
+ Applicability::MachineApplicable,
+ );
+ }
}
extract_msrv_attr!(LateContext);
diff --git a/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs b/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs
index 0e22485db..fc8f23630 100644
--- a/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs
@@ -1,8 +1,9 @@
+use clippy_config::msrvs::{self, Msrv};
use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then};
use clippy_utils::is_doc_hidden;
-use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::source::snippet_opt;
use rustc_ast::ast::{self, VisibilityKind};
+use rustc_ast::attr;
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::Applicability;
use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
@@ -21,7 +22,7 @@ declare_clippy_lint! {
/// and allows possible optimizations when applied to enums.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// struct S {
/// pub a: i32,
/// pub b: i32,
@@ -38,7 +39,7 @@ declare_clippy_lint! {
/// struct T(pub i32, pub i32, ());
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// #[non_exhaustive]
/// struct S {
/// pub a: i32,
@@ -137,7 +138,7 @@ impl EarlyLintPass for ManualNonExhaustiveStruct {
);
}
diag.span_help(field.span, "remove this field");
- }
+ },
);
}
}
@@ -158,7 +159,8 @@ impl<'tcx> LateLintPass<'tcx> for ManualNonExhaustiveEnum {
let mut iter = def.variants.iter().filter_map(|v| {
(matches!(v.data, hir::VariantData::Unit(_, _))
&& v.ident.as_str().starts_with('_')
- && is_doc_hidden(cx.tcx.hir().attrs(v.hir_id)))
+ && is_doc_hidden(cx.tcx.hir().attrs(v.hir_id))
+ && !attr::contains_name(cx.tcx.hir().attrs(item.hir_id()), sym::non_exhaustive))
.then_some((v.def_id, v.span))
});
if let Some((id, span)) = iter.next()
@@ -198,16 +200,14 @@ impl<'tcx> LateLintPass<'tcx> for ManualNonExhaustiveEnum {
enum_span,
"this seems like a manual implementation of the non-exhaustive pattern",
|diag| {
- if !cx.tcx.adt_def(enum_id).is_variant_list_non_exhaustive()
- && let header_span = cx.sess().source_map().span_until_char(enum_span, '{')
- && let Some(snippet) = snippet_opt(cx, header_span)
- {
- diag.span_suggestion(
- header_span,
- "add the attribute",
- format!("#[non_exhaustive] {snippet}"),
- Applicability::Unspecified,
- );
+ let header_span = cx.sess().source_map().span_until_char(enum_span, '{');
+ if let Some(snippet) = snippet_opt(cx, header_span) {
+ diag.span_suggestion(
+ header_span,
+ "add the attribute",
+ format!("#[non_exhaustive] {snippet}"),
+ Applicability::Unspecified,
+ );
}
diag.span_help(variant_span, "remove this variant");
},
diff --git a/src/tools/clippy/clippy_lints/src/manual_range_patterns.rs b/src/tools/clippy/clippy_lints/src/manual_range_patterns.rs
index 90557b555..d24bfe182 100644
--- a/src/tools/clippy/clippy_lints/src/manual_range_patterns.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_range_patterns.rs
@@ -22,12 +22,12 @@ declare_clippy_lint! {
/// in order to support negative numbers.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let x = 6;
/// let foo = matches!(x, 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10);
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// let x = 6;
/// let foo = matches!(x, 1..=10);
/// ```
diff --git a/src/tools/clippy/clippy_lints/src/manual_rem_euclid.rs b/src/tools/clippy/clippy_lints/src/manual_rem_euclid.rs
index 0e89ca132..bc8372fbd 100644
--- a/src/tools/clippy/clippy_lints/src/manual_rem_euclid.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_rem_euclid.rs
@@ -1,6 +1,6 @@
+use clippy_config::msrvs::{self, Msrv};
use clippy_utils::consts::{constant_full_int, FullInt};
use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::source::snippet_with_context;
use clippy_utils::{in_constant, path_to_local};
use rustc_errors::Applicability;
@@ -18,12 +18,12 @@ declare_clippy_lint! {
/// It's simpler and more readable.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let x: i32 = 24;
/// let rem = ((x % 4) + 4) % 4;
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// let x: i32 = 24;
/// let rem = x.rem_euclid(4);
/// ```
@@ -76,30 +76,31 @@ impl<'tcx> LateLintPass<'tcx> for ManualRemEuclid {
// Also ensures the const is nonzero since zero can't be a divisor
&& const1 == const2 && const2 == const3
&& let Some(hir_id) = path_to_local(expr3)
- && let Some(Node::Pat(_)) = cx.tcx.hir().find(hir_id) {
- // Apply only to params or locals with annotated types
- match cx.tcx.hir().find_parent(hir_id) {
- Some(Node::Param(..)) => (),
- Some(Node::Local(local)) => {
- let Some(ty) = local.ty else { return };
- if matches!(ty.kind, TyKind::Infer) {
- return;
- }
+ && let Some(Node::Pat(_)) = cx.tcx.hir().find(hir_id)
+ {
+ // Apply only to params or locals with annotated types
+ match cx.tcx.hir().find_parent(hir_id) {
+ Some(Node::Param(..)) => (),
+ Some(Node::Local(local)) => {
+ let Some(ty) = local.ty else { return };
+ if matches!(ty.kind, TyKind::Infer) {
+ return;
}
- _ => return,
- };
+ },
+ _ => return,
+ };
- let mut app = Applicability::MachineApplicable;
- let rem_of = snippet_with_context(cx, expr3.span, ctxt, "_", &mut app).0;
- span_lint_and_sugg(
- cx,
- MANUAL_REM_EUCLID,
- expr.span,
- "manual `rem_euclid` implementation",
- "consider using",
- format!("{rem_of}.rem_euclid({const1})"),
- app,
- );
+ let mut app = Applicability::MachineApplicable;
+ let rem_of = snippet_with_context(cx, expr3.span, ctxt, "_", &mut app).0;
+ span_lint_and_sugg(
+ cx,
+ MANUAL_REM_EUCLID,
+ expr.span,
+ "manual `rem_euclid` implementation",
+ "consider using",
+ format!("{rem_of}.rem_euclid({const1})"),
+ app,
+ );
}
}
diff --git a/src/tools/clippy/clippy_lints/src/manual_retain.rs b/src/tools/clippy/clippy_lints/src/manual_retain.rs
index 1a69a48c5..2f8682d04 100644
--- a/src/tools/clippy/clippy_lints/src/manual_retain.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_retain.rs
@@ -1,5 +1,5 @@
+use clippy_config::msrvs::{self, Msrv};
use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::source::snippet;
use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item};
use clippy_utils::{get_parent_expr, match_def_path, paths, SpanlessEq};
@@ -35,13 +35,13 @@ declare_clippy_lint! {
/// ### Why is this bad?
/// `.retain()` is simpler and avoids needless allocation.
/// ### Example
- /// ```rust
+ /// ```no_run
/// let mut vec = vec![0, 1, 2];
/// vec = vec.iter().filter(|&x| x % 2 == 0).copied().collect();
/// vec = vec.into_iter().filter(|x| x % 2 == 0).collect();
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// let mut vec = vec![0, 1, 2];
/// vec.retain(|x| x % 2 == 0);
/// ```
@@ -97,7 +97,8 @@ fn check_into_iter(
&& let Some(into_iter_def_id) = cx.typeck_results().type_dependent_def_id(into_iter_expr.hir_id)
&& Some(into_iter_def_id) == cx.tcx.lang_items().into_iter_fn()
&& match_acceptable_type(cx, left_expr, msrv)
- && SpanlessEq::new(cx).eq_expr(left_expr, struct_expr) {
+ && SpanlessEq::new(cx).eq_expr(left_expr, struct_expr)
+ {
suggest(cx, parent_expr, left_expr, target_expr);
}
}
@@ -120,7 +121,8 @@ fn check_iter(
&& let Some(iter_expr_def_id) = cx.typeck_results().type_dependent_def_id(iter_expr.hir_id)
&& match_acceptable_def_path(cx, iter_expr_def_id)
&& match_acceptable_type(cx, left_expr, msrv)
- && SpanlessEq::new(cx).eq_expr(left_expr, struct_expr) {
+ && SpanlessEq::new(cx).eq_expr(left_expr, struct_expr)
+ {
suggest(cx, parent_expr, left_expr, filter_expr);
}
}
@@ -135,7 +137,7 @@ fn check_to_owned(
if msrv.meets(msrvs::STRING_RETAIN)
&& let hir::ExprKind::MethodCall(_, filter_expr, [], _) = &target_expr.kind
&& let Some(to_owned_def_id) = cx.typeck_results().type_dependent_def_id(target_expr.hir_id)
- && match_def_path(cx, to_owned_def_id, &paths::TO_OWNED_METHOD)
+ && cx.tcx.is_diagnostic_item(sym::to_owned_method, to_owned_def_id)
&& let hir::ExprKind::MethodCall(_, chars_expr, [_], _) = &filter_expr.kind
&& let Some(filter_def_id) = cx.typeck_results().type_dependent_def_id(filter_expr.hir_id)
&& match_def_path(cx, filter_def_id, &paths::CORE_ITER_FILTER)
@@ -144,33 +146,35 @@ fn check_to_owned(
&& match_def_path(cx, chars_expr_def_id, &paths::STR_CHARS)
&& let ty = cx.typeck_results().expr_ty(str_expr).peel_refs()
&& is_type_lang_item(cx, ty, hir::LangItem::String)
- && SpanlessEq::new(cx).eq_expr(left_expr, str_expr) {
+ && SpanlessEq::new(cx).eq_expr(left_expr, str_expr)
+ {
suggest(cx, parent_expr, left_expr, filter_expr);
}
}
fn suggest(cx: &LateContext<'_>, parent_expr: &hir::Expr<'_>, left_expr: &hir::Expr<'_>, filter_expr: &hir::Expr<'_>) {
if let hir::ExprKind::MethodCall(_, _, [closure], _) = filter_expr.kind
- && let hir::ExprKind::Closure(&hir::Closure { body, ..}) = closure.kind
+ && let hir::ExprKind::Closure(&hir::Closure { body, .. }) = closure.kind
&& let filter_body = cx.tcx.hir().body(body)
&& let [filter_params] = filter_body.params
&& let Some(sugg) = match filter_params.pat.kind {
- hir::PatKind::Binding(_, _, filter_param_ident, None) => {
- Some(format!("{}.retain(|{filter_param_ident}| {})", snippet(cx, left_expr.span, ".."), snippet(cx, filter_body.value.span, "..")))
- },
- hir::PatKind::Tuple([key_pat, value_pat], _) => {
- make_sugg(cx, key_pat, value_pat, left_expr, filter_body)
- },
- hir::PatKind::Ref(pat, _) => {
- match pat.kind {
- hir::PatKind::Binding(_, _, filter_param_ident, None) => {
- Some(format!("{}.retain(|{filter_param_ident}| {})", snippet(cx, left_expr.span, ".."), snippet(cx, filter_body.value.span, "..")))
- },
- _ => None
- }
+ hir::PatKind::Binding(_, _, filter_param_ident, None) => Some(format!(
+ "{}.retain(|{filter_param_ident}| {})",
+ snippet(cx, left_expr.span, ".."),
+ snippet(cx, filter_body.value.span, "..")
+ )),
+ hir::PatKind::Tuple([key_pat, value_pat], _) => make_sugg(cx, key_pat, value_pat, left_expr, filter_body),
+ hir::PatKind::Ref(pat, _) => match pat.kind {
+ hir::PatKind::Binding(_, _, filter_param_ident, None) => Some(format!(
+ "{}.retain(|{filter_param_ident}| {})",
+ snippet(cx, left_expr.span, ".."),
+ snippet(cx, filter_body.value.span, "..")
+ )),
+ _ => None,
},
- _ => None
- } {
+ _ => None,
+ }
+ {
span_lint_and_sugg(
cx,
MANUAL_RETAIN,
@@ -178,7 +182,7 @@ fn suggest(cx: &LateContext<'_>, parent_expr: &hir::Expr<'_>, left_expr: &hir::E
"this expression can be written more simply using `.retain()`",
"consider calling `.retain()` instead",
sugg,
- Applicability::MachineApplicable
+ Applicability::MachineApplicable,
);
}
}
diff --git a/src/tools/clippy/clippy_lints/src/manual_slice_size_calculation.rs b/src/tools/clippy/clippy_lints/src/manual_slice_size_calculation.rs
index f97600b53..3b97d1659 100644
--- a/src/tools/clippy/clippy_lints/src/manual_slice_size_calculation.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_slice_size_calculation.rs
@@ -22,12 +22,12 @@ declare_clippy_lint! {
/// * Less turbofishing
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # let data : &[i32] = &[1, 2, 3];
/// let newlen = data.len() * std::mem::size_of::<i32>();
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # let data : &[i32] = &[1, 2, 3];
/// let newlen = std::mem::size_of_val(data);
/// ```
diff --git a/src/tools/clippy/clippy_lints/src/manual_string_new.rs b/src/tools/clippy/clippy_lints/src/manual_string_new.rs
index c20d7959f..f8afae0e1 100644
--- a/src/tools/clippy/clippy_lints/src/manual_string_new.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_string_new.rs
@@ -19,12 +19,12 @@ declare_clippy_lint! {
/// be confusing.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let a = "".to_string();
/// let b: String = "".into();
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// let a = String::new();
/// let b = String::new();
/// ```
@@ -65,9 +65,9 @@ impl LateLintPass<'_> for ManualStringNew {
/// Checks if an expression's kind corresponds to an empty &str.
fn is_expr_kind_empty_str(expr_kind: &ExprKind<'_>) -> bool {
- if let ExprKind::Lit(lit) = expr_kind &&
- let LitKind::Str(value, _) = lit.node &&
- value == symbol::kw::Empty
+ if let ExprKind::Lit(lit) = expr_kind
+ && let LitKind::Str(value, _) = lit.node
+ && value == symbol::kw::Empty
{
return true;
}
@@ -110,23 +110,22 @@ fn parse_call(cx: &LateContext<'_>, span: Span, func: &Expr<'_>, args: &[Expr<'_
if let ExprKind::Path(qpath) = &func.kind {
if let QPath::TypeRelative(_, _) = qpath {
// String::from(...) or String::try_from(...)
- if let QPath::TypeRelative(ty, path_seg) = qpath &&
- [sym::from, sym::try_from].contains(&path_seg.ident.name) &&
- let TyKind::Path(qpath) = &ty.kind &&
- let QPath::Resolved(_, path) = qpath &&
- let [path_seg] = path.segments &&
- path_seg.ident.name == sym::String &&
- is_expr_kind_empty_str(arg_kind)
+ if let QPath::TypeRelative(ty, path_seg) = qpath
+ && [sym::from, sym::try_from].contains(&path_seg.ident.name)
+ && let TyKind::Path(qpath) = &ty.kind
+ && let QPath::Resolved(_, path) = qpath
+ && let [path_seg] = path.segments
+ && path_seg.ident.name == sym::String
+ && is_expr_kind_empty_str(arg_kind)
{
warn_then_suggest(cx, span);
}
} else if let QPath::Resolved(_, path) = qpath {
// From::from(...) or TryFrom::try_from(...)
- if let [path_seg1, path_seg2] = path.segments &&
- is_expr_kind_empty_str(arg_kind) && (
- (path_seg1.ident.name == sym::From && path_seg2.ident.name == sym::from) ||
- (path_seg1.ident.name == sym::TryFrom && path_seg2.ident.name == sym::try_from)
- )
+ if let [path_seg1, path_seg2] = path.segments
+ && is_expr_kind_empty_str(arg_kind)
+ && ((path_seg1.ident.name == sym::From && path_seg2.ident.name == sym::from)
+ || (path_seg1.ident.name == sym::TryFrom && path_seg2.ident.name == sym::try_from))
{
warn_then_suggest(cx, span);
}
diff --git a/src/tools/clippy/clippy_lints/src/manual_strip.rs b/src/tools/clippy/clippy_lints/src/manual_strip.rs
index 201bb56ef..9a9e6af50 100644
--- a/src/tools/clippy/clippy_lints/src/manual_strip.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_strip.rs
@@ -1,6 +1,6 @@
+use clippy_config::msrvs::{self, Msrv};
use clippy_utils::consts::{constant, Constant};
use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then};
-use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::source::snippet;
use clippy_utils::usage::mutated_variables;
use clippy_utils::{eq_expr_value, higher, match_def_path, paths};
@@ -27,14 +27,14 @@ declare_clippy_lint! {
/// used by `str::{starts,ends}_with` and in the slicing.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let s = "hello, world!";
/// if s.starts_with("hello, ") {
/// assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!");
/// }
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// let s = "hello, world!";
/// if let Some(end) = s.strip_prefix("hello, ") {
/// assert_eq!(end.to_uppercase(), "WORLD!");
diff --git a/src/tools/clippy/clippy_lints/src/map_unit_fn.rs b/src/tools/clippy/clippy_lints/src/map_unit_fn.rs
index f0a0f482a..817d072b9 100644
--- a/src/tools/clippy/clippy_lints/src/map_unit_fn.rs
+++ b/src/tools/clippy/clippy_lints/src/map_unit_fn.rs
@@ -8,8 +8,7 @@ use rustc_hir as hir;
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::{self, Ty};
use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::source_map::Span;
-use rustc_span::sym;
+use rustc_span::{sym, Span};
declare_clippy_lint! {
/// ### What it does
@@ -21,7 +20,7 @@ declare_clippy_lint! {
/// an if let statement
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # fn do_stuff() -> Option<String> { Some(String::new()) }
/// # fn log_err_msg(foo: String) -> Option<String> { Some(foo) }
/// # fn format_msg(foo: String) -> String { String::new() }
@@ -33,7 +32,7 @@ declare_clippy_lint! {
///
/// The correct use would be:
///
- /// ```rust
+ /// ```no_run
/// # fn do_stuff() -> Option<String> { Some(String::new()) }
/// # fn log_err_msg(foo: String) -> Option<String> { Some(foo) }
/// # fn format_msg(foo: String) -> String { String::new() }
@@ -63,7 +62,7 @@ declare_clippy_lint! {
/// an if let statement
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # fn do_stuff() -> Result<String, String> { Ok(String::new()) }
/// # fn log_err_msg(foo: String) -> Result<String, String> { Ok(foo) }
/// # fn format_msg(foo: String) -> String { String::new() }
@@ -75,7 +74,7 @@ declare_clippy_lint! {
///
/// The correct use would be:
///
- /// ```rust
+ /// ```no_run
/// # fn do_stuff() -> Result<String, String> { Ok(String::new()) }
/// # fn log_err_msg(foo: String) -> Result<String, String> { Ok(foo) }
/// # fn format_msg(foo: String) -> String { String::new() }
diff --git a/src/tools/clippy/clippy_lints/src/matches/collapsible_match.rs b/src/tools/clippy/clippy_lints/src/matches/collapsible_match.rs
index 33a052c41..29b935fb6 100644
--- a/src/tools/clippy/clippy_lints/src/matches/collapsible_match.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/collapsible_match.rs
@@ -57,7 +57,7 @@ fn check_arm<'tcx>(
}
},
};
- if outer_pat.span.ctxt() == inner_scrutinee.span.ctxt();
+ if outer_pat.span.eq_ctxt(inner_scrutinee.span);
// match expression must be a local binding
// match <local> { .. }
if let Some(binding_id) = path_to_local(peel_ref_operators(cx, inner_scrutinee));
diff --git a/src/tools/clippy/clippy_lints/src/matches/manual_filter.rs b/src/tools/clippy/clippy_lints/src/matches/manual_filter.rs
index e0181a475..cdb51c33a 100644
--- a/src/tools/clippy/clippy_lints/src/matches/manual_filter.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/manual_filter.rs
@@ -99,12 +99,20 @@ pub(super) fn check_match<'tcx>(
) {
let ty = cx.typeck_results().expr_ty(expr);
if is_type_diagnostic_item(cx, ty, sym::Option)
- && let [first_arm, second_arm] = arms
- && first_arm.guard.is_none()
- && second_arm.guard.is_none()
- {
- check(cx, expr, scrutinee, first_arm.pat, first_arm.body, Some(second_arm.pat), second_arm.body);
- }
+ && let [first_arm, second_arm] = arms
+ && first_arm.guard.is_none()
+ && second_arm.guard.is_none()
+ {
+ check(
+ cx,
+ expr,
+ scrutinee,
+ first_arm.pat,
+ first_arm.body,
+ Some(second_arm.pat),
+ second_arm.body,
+ );
+ }
}
pub(super) fn check_if_let<'tcx>(
diff --git a/src/tools/clippy/clippy_lints/src/matches/manual_utils.rs b/src/tools/clippy/clippy_lints/src/matches/manual_utils.rs
index 6b611f567..781ee138c 100644
--- a/src/tools/clippy/clippy_lints/src/matches/manual_utils.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/manual_utils.rs
@@ -119,7 +119,7 @@ where
// it's being passed by value.
let scrutinee = peel_hir_expr_refs(scrutinee).0;
let (scrutinee_str, _) = snippet_with_context(cx, scrutinee.span, expr_ctxt, "..", &mut app);
- let scrutinee_str = if scrutinee.span.ctxt() == expr.span.ctxt() && scrutinee.precedence().order() < PREC_POSTFIX {
+ let scrutinee_str = if scrutinee.span.eq_ctxt(expr.span) && scrutinee.precedence().order() < PREC_POSTFIX {
format!("({scrutinee_str})")
} else {
scrutinee_str.into()
@@ -130,7 +130,7 @@ where
if_chain! {
if !some_expr.needs_unsafe_block;
if let Some(func) = can_pass_as_func(cx, id, some_expr.expr);
- if func.span.ctxt() == some_expr.expr.span.ctxt();
+ if func.span.eq_ctxt(some_expr.expr.span);
then {
snippet_with_applicability(cx, func.span, "..", &mut app).into_owned()
} else {
diff --git a/src/tools/clippy/clippy_lints/src/matches/match_wild_err_arm.rs b/src/tools/clippy/clippy_lints/src/matches/match_wild_err_arm.rs
index de911f7a0..a2903e52a 100644
--- a/src/tools/clippy/clippy_lints/src/matches/match_wild_err_arm.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/match_wild_err_arm.rs
@@ -19,7 +19,7 @@ pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, ex: &Expr<'tcx>, arms: &[Arm<'
if is_type_diagnostic_item(cx, ex_ty, sym::Result) {
for arm in arms {
if let PatKind::TupleStruct(ref path, inner, _) = arm.pat.kind {
- let path_str = rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_qpath(path, false));
+ let path_str = rustc_hir_pretty::qpath_to_string(path);
if path_str == "Err" {
let mut matching_wild = inner.iter().any(is_wild);
let mut ident_bind_name = kw::Underscore;
diff --git a/src/tools/clippy/clippy_lints/src/matches/mod.rs b/src/tools/clippy/clippy_lints/src/matches/mod.rs
index 930386a60..dea46d4d3 100644
--- a/src/tools/clippy/clippy_lints/src/matches/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/mod.rs
@@ -24,7 +24,7 @@ mod single_match;
mod try_err;
mod wild_in_or_pats;
-use clippy_utils::msrvs::{self, Msrv};
+use clippy_config::msrvs::{self, Msrv};
use clippy_utils::source::{snippet_opt, walk_span_to_context};
use clippy_utils::{higher, in_constant, is_direct_expn_of, is_span_match, tokenize_with_text};
use rustc_hir::{Arm, Expr, ExprKind, Local, MatchSource, Pat};
@@ -48,7 +48,7 @@ declare_clippy_lint! {
/// Just readability – `if let` nests less than a `match`.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # fn bar(stool: &str) {}
/// # let x = Some("abc");
/// match x {
@@ -58,7 +58,7 @@ declare_clippy_lint! {
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # fn bar(stool: &str) {}
/// # let x = Some("abc");
/// if let Some(ref foo) = x {
@@ -85,7 +85,7 @@ declare_clippy_lint! {
/// ### Example
/// Using `match`:
///
- /// ```rust
+ /// ```no_run
/// # fn bar(foo: &usize) {}
/// # let other_ref: usize = 1;
/// # let x: Option<&usize> = Some(&1);
@@ -97,7 +97,7 @@ declare_clippy_lint! {
///
/// Using `if let` with `else`:
///
- /// ```rust
+ /// ```no_run
/// # fn bar(foo: &usize) {}
/// # let other_ref: usize = 1;
/// # let x: Option<&usize> = Some(&1);
@@ -155,7 +155,7 @@ declare_clippy_lint! {
/// It makes the code less readable.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # fn foo() {}
/// # fn bar() {}
/// let condition: bool = true;
@@ -165,7 +165,7 @@ declare_clippy_lint! {
/// }
/// ```
/// Use if/else instead:
- /// ```rust
+ /// ```no_run
/// # fn foo() {}
/// # fn bar() {}
/// let condition: bool = true;
@@ -190,7 +190,7 @@ declare_clippy_lint! {
/// less obvious.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let x = 5;
/// match x {
/// 1..=10 => println!("1 ... 10"),
@@ -214,7 +214,7 @@ declare_clippy_lint! {
/// catching all exceptions in java with `catch(Exception)`
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let x: Result<i32, &str> = Ok(3);
/// match x {
/// Ok(_) => println!("ok"),
@@ -236,7 +236,7 @@ declare_clippy_lint! {
/// Using `as_ref()` or `as_mut()` instead is shorter.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let x: Option<()> = None;
///
/// let r: Option<&()> = match x {
@@ -246,7 +246,7 @@ declare_clippy_lint! {
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// let x: Option<()> = None;
///
/// let r: Option<&()> = x.as_ref();
@@ -269,7 +269,7 @@ declare_clippy_lint! {
/// variants, and also may not use correct path to enum if it's not present in the current scope.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # enum Foo { A(usize), B(usize) }
/// # let x = Foo::B(1);
/// match x {
@@ -279,7 +279,7 @@ declare_clippy_lint! {
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # enum Foo { A(usize), B(usize) }
/// # let x = Foo::B(1);
/// match x {
@@ -305,7 +305,7 @@ declare_clippy_lint! {
/// if it's not present in the current scope.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # enum Foo { A, B, C }
/// # let x = Foo::B;
/// match x {
@@ -316,7 +316,7 @@ declare_clippy_lint! {
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # enum Foo { A, B, C }
/// # let x = Foo::B;
/// match x {
@@ -340,7 +340,7 @@ declare_clippy_lint! {
/// It makes the code less readable, especially to spot wildcard pattern use in match arm.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # let s = "foo";
/// match s {
/// "a" => {},
@@ -349,7 +349,7 @@ declare_clippy_lint! {
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # let s = "foo";
/// match s {
/// "a" => {},
@@ -371,7 +371,7 @@ declare_clippy_lint! {
/// Just readability – `let` doesn't nest, whereas a `match` does.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// enum Wrapper {
/// Data(i32),
/// }
@@ -384,7 +384,7 @@ declare_clippy_lint! {
/// ```
///
/// The correct use would be:
- /// ```rust
+ /// ```no_run
/// enum Wrapper {
/// Data(i32),
/// }
@@ -410,7 +410,7 @@ declare_clippy_lint! {
/// is actually binding temporary value, bringing a 'dropped while borrowed' error.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # let a = 1;
/// # let b = 2;
/// match (a, b) {
@@ -421,7 +421,7 @@ declare_clippy_lint! {
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # let a = 1;
/// # let b = 2;
/// let (c, d) = (a, b);
@@ -441,7 +441,7 @@ declare_clippy_lint! {
/// matching all enum variants explicitly.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # struct A { a: i32 }
/// let a = A { a: 5 };
///
@@ -452,7 +452,7 @@ declare_clippy_lint! {
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # struct A { a: i32 }
/// # let a = A { a: 5 };
/// match a {
@@ -484,7 +484,7 @@ declare_clippy_lint! {
/// drop order.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # use std::task::Poll;
/// # use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
/// if let Ok(_) = Ok::<i32, i32>(42) {}
@@ -503,7 +503,7 @@ declare_clippy_lint! {
///
/// The more idiomatic use would be:
///
- /// ```rust
+ /// ```no_run
/// # use std::task::Poll;
/// # use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
/// if Ok::<i32, i32>(42).is_ok() {}
@@ -535,7 +535,7 @@ declare_clippy_lint! {
/// `cfg` attributes that remove an arm evaluating to `false`.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let x = Some(5);
///
/// let a = match x {
@@ -551,7 +551,7 @@ declare_clippy_lint! {
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// let x = Some(5);
/// let a = matches!(x, Some(0));
/// ```
@@ -664,7 +664,7 @@ declare_clippy_lint! {
/// It is unnecessarily verbose and complex.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// fn func(opt: Option<Result<u64, String>>) {
/// let n = match opt {
/// Some(n) => match n {
@@ -676,7 +676,7 @@ declare_clippy_lint! {
/// }
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// fn func(opt: Option<Result<u64, String>>) {
/// let n = match opt {
/// Some(Ok(n)) => n,
@@ -698,7 +698,7 @@ declare_clippy_lint! {
/// Concise code helps focusing on behavior instead of boilerplate.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let foo: Option<i32> = None;
/// match foo {
/// Some(v) => v,
@@ -707,7 +707,7 @@ declare_clippy_lint! {
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// let foo: Option<i32> = None;
/// foo.unwrap_or(1);
/// ```
@@ -761,7 +761,7 @@ declare_clippy_lint! {
/// The arm is unreachable, which is likely a mistake
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # let text = "Foo";
/// match &*text.to_ascii_lowercase() {
/// "foo" => {},
@@ -770,7 +770,7 @@ declare_clippy_lint! {
/// }
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # let text = "Foo";
/// match &*text.to_ascii_lowercase() {
/// "foo" => {},
@@ -823,7 +823,7 @@ declare_clippy_lint! {
/// println!("All done!");
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # use std::sync::Mutex;
/// # struct State {}
/// # impl State {
@@ -861,7 +861,7 @@ declare_clippy_lint! {
/// always return), it is more clear to write `return Err(x)`.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// fn foo(fail: bool) -> Result<i32, String> {
/// if fail {
/// Err("failed")?;
@@ -871,7 +871,7 @@ declare_clippy_lint! {
/// ```
/// Could be written:
///
- /// ```rust
+ /// ```no_run
/// fn foo(fail: bool) -> Result<i32, String> {
/// if fail {
/// return Err("failed".into());
@@ -893,14 +893,14 @@ declare_clippy_lint! {
/// Using the `map` method is clearer and more concise.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// match Some(0) {
/// Some(x) => Some(x + 1),
/// None => None,
/// };
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// Some(0).map(|x| x + 1);
/// ```
#[clippy::version = "1.52.0"]
@@ -917,7 +917,7 @@ declare_clippy_lint! {
/// Using the `filter` method is clearer and more concise.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// match Some(0) {
/// Some(x) => if x % 2 == 0 {
/// Some(x)
@@ -928,7 +928,7 @@ declare_clippy_lint! {
/// };
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// Some(0).filter(|&x| x % 2 == 0);
/// ```
#[clippy::version = "1.66.0"]
@@ -961,13 +961,12 @@ declare_clippy_lint! {
/// _ => todo!(),
/// }
/// ```
- #[clippy::version = "1.72.0"]
+ #[clippy::version = "1.73.0"]
pub REDUNDANT_GUARDS,
complexity,
"checks for unnecessary guards in match expressions"
}
-#[derive(Default)]
pub struct Matches {
msrv: Msrv,
infallible_destructuring_match_linted: bool,
@@ -978,7 +977,7 @@ impl Matches {
pub fn new(msrv: Msrv) -> Self {
Self {
msrv,
- ..Matches::default()
+ infallible_destructuring_match_linted: false,
}
}
}
diff --git a/src/tools/clippy/clippy_lints/src/matches/overlapping_arms.rs b/src/tools/clippy/clippy_lints/src/matches/overlapping_arms.rs
index 7c0485914..8f0083f81 100644
--- a/src/tools/clippy/clippy_lints/src/matches/overlapping_arms.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/overlapping_arms.rs
@@ -1,4 +1,4 @@
-use clippy_utils::consts::{constant, constant_full_int, miri_to_const, FullInt};
+use clippy_utils::consts::{constant, constant_full_int, mir_to_const, FullInt};
use clippy_utils::diagnostics::span_lint_and_note;
use core::cmp::Ordering;
use rustc_hir::{Arm, Expr, PatKind, RangeEnd};
@@ -37,14 +37,14 @@ fn all_ranges<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>], ty: Ty<'tcx>)
Some(lhs) => constant(cx, cx.typeck_results(), lhs)?,
None => {
let min_val_const = ty.numeric_min_val(cx.tcx)?;
- miri_to_const(cx, mir::Const::from_ty_const(min_val_const, cx.tcx))?
+ mir_to_const(cx, mir::Const::from_ty_const(min_val_const, cx.tcx))?
},
};
let rhs_const = match rhs {
Some(rhs) => constant(cx, cx.typeck_results(), rhs)?,
None => {
let max_val_const = ty.numeric_max_val(cx.tcx)?;
- miri_to_const(cx, mir::Const::from_ty_const(max_val_const, cx.tcx))?
+ mir_to_const(cx, mir::Const::from_ty_const(max_val_const, cx.tcx))?
},
};
let lhs_val = lhs_const.int_value(cx, ty)?;
diff --git a/src/tools/clippy/clippy_lints/src/matches/redundant_guards.rs b/src/tools/clippy/clippy_lints/src/matches/redundant_guards.rs
index 0efeeacc9..4a44d596a 100644
--- a/src/tools/clippy/clippy_lints/src/matches/redundant_guards.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/redundant_guards.rs
@@ -27,8 +27,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'tcx>]) {
arm,
Arm {
pat: Pat {
- kind: PatKind::Wild,
- ..
+ kind: PatKind::Wild, ..
},
..
},
@@ -42,14 +41,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'tcx>]) {
(PatKind::Ref(..), None) | (_, Some(_)) => continue,
_ => arm.pat.span,
};
- emit_redundant_guards(
- cx,
- outer_arm,
- if_expr.span,
- pat_span,
- &binding,
- arm.guard,
- );
+ emit_redundant_guards(cx, outer_arm, if_expr.span, pat_span, &binding, arm.guard);
}
// `Some(x) if let Some(2) = x`
else if let Guard::IfLet(let_expr) = guard
@@ -60,14 +52,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'tcx>]) {
(PatKind::Ref(..), None) | (_, Some(_)) => continue,
_ => let_expr.pat.span,
};
- emit_redundant_guards(
- cx,
- outer_arm,
- let_expr.span,
- pat_span,
- &binding,
- None,
- );
+ emit_redundant_guards(cx, outer_arm, let_expr.span, pat_span, &binding, None);
}
// `Some(x) if x == Some(2)`
// `Some(x) if Some(2) == x`
@@ -93,14 +78,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'tcx>]) {
(ExprKind::AddrOf(..), None) | (_, Some(_)) => continue,
_ => pat.span,
};
- emit_redundant_guards(
- cx,
- outer_arm,
- if_expr.span,
- pat_span,
- &binding,
- None,
- );
+ emit_redundant_guards(cx, outer_arm, if_expr.span, pat_span, &binding, None);
}
}
}
@@ -116,7 +94,9 @@ fn get_pat_binding<'tcx>(
guard_expr: &Expr<'_>,
outer_arm: &Arm<'tcx>,
) -> Option<PatBindingInfo> {
- if let Some(local) = path_to_local(guard_expr) && !is_local_used(cx, outer_arm.body, local) {
+ if let Some(local) = path_to_local(guard_expr)
+ && !is_local_used(cx, outer_arm.body, local)
+ {
let mut span = None;
let mut byref_ident = None;
let mut multiple_bindings = false;
@@ -223,10 +203,7 @@ fn expr_can_be_pat(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
Res::Def(DefKind::Struct | DefKind::Enum | DefKind::Ctor(..), ..),
)
},
- ExprKind::AddrOf(..)
- | ExprKind::Array(..)
- | ExprKind::Tup(..)
- | ExprKind::Struct(..) => true,
+ ExprKind::AddrOf(..) | ExprKind::Array(..) | ExprKind::Tup(..) | ExprKind::Struct(..) => true,
ExprKind::Lit(lit) if !matches!(lit.node, LitKind::Float(..)) => true,
_ => false,
} {
diff --git a/src/tools/clippy/clippy_lints/src/matches/single_match.rs b/src/tools/clippy/clippy_lints/src/matches/single_match.rs
index 6b05c6bff..48efd0230 100644
--- a/src/tools/clippy/clippy_lints/src/matches/single_match.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/single_match.rs
@@ -20,8 +20,7 @@ fn empty_arm_has_comment(cx: &LateContext<'_>, span: Span) -> bool {
if let Some(ff) = get_source_text(cx, span)
&& let Some(text) = ff.as_str()
{
- text.as_bytes().windows(2)
- .any(|w| w == b"//" || w == b"/*")
+ text.as_bytes().windows(2).any(|w| w == b"//" || w == b"/*")
} else {
false
}
@@ -51,7 +50,7 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr:
// block with 2+ statements or 1 expr and 1+ statement
Some(els)
} else {
- // not a block or an emtpy block w/ comments, don't lint
+ // not a block or an empty block w/ comments, don't lint
return;
};
diff --git a/src/tools/clippy/clippy_lints/src/mem_replace.rs b/src/tools/clippy/clippy_lints/src/mem_replace.rs
index 8a921d4af..760c8f59c 100644
--- a/src/tools/clippy/clippy_lints/src/mem_replace.rs
+++ b/src/tools/clippy/clippy_lints/src/mem_replace.rs
@@ -1,5 +1,5 @@
+use clippy_config::msrvs::{self, Msrv};
use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg, span_lint_and_then};
-use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::source::{snippet, snippet_with_applicability};
use clippy_utils::sugg::Sugg;
use clippy_utils::ty::is_non_aggregate_primitive_type;
@@ -11,7 +11,7 @@ use rustc_hir::{Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::lint::in_external_macro;
use rustc_session::{declare_tool_lint, impl_lint_pass};
-use rustc_span::source_map::Span;
+use rustc_span::Span;
use rustc_span::symbol::sym;
declare_clippy_lint! {
@@ -25,14 +25,14 @@ declare_clippy_lint! {
/// `None`.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// use std::mem;
///
/// let mut an_option = Some(0);
/// let replaced = mem::replace(&mut an_option, None);
/// ```
/// Is better expressed with:
- /// ```rust
+ /// ```no_run
/// let mut an_option = Some(0);
/// let taken = an_option.take();
/// ```
@@ -53,7 +53,7 @@ declare_clippy_lint! {
/// observed in the case of a panic.
///
/// ### Example
- /// ```
+ /// ```no_run
/// use std::mem;
///# fn may_panic(v: Vec<i32>) -> Vec<i32> { v }
///
@@ -84,12 +84,12 @@ declare_clippy_lint! {
/// take the current value and replace it with the default value of that type.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let mut text = String::from("foo");
/// let replaced = std::mem::replace(&mut text, String::default());
/// ```
/// Is better expressed with:
- /// ```rust
+ /// ```no_run
/// let mut text = String::from("foo");
/// let taken = std::mem::take(&mut text);
/// ```
diff --git a/src/tools/clippy/clippy_lints/src/methods/bytecount.rs b/src/tools/clippy/clippy_lints/src/methods/bytecount.rs
index f490a7175..35370355f 100644
--- a/src/tools/clippy/clippy_lints/src/methods/bytecount.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/bytecount.rs
@@ -1,8 +1,8 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_with_applicability;
-use clippy_utils::ty::match_type;
+use clippy_utils::ty::is_type_diagnostic_item;
use clippy_utils::visitors::is_local_used;
-use clippy_utils::{path_to_local_id, paths, peel_blocks, peel_ref_operators, strip_pat_refs};
+use clippy_utils::{path_to_local_id, peel_blocks, peel_ref_operators, strip_pat_refs};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{BinOpKind, Closure, Expr, ExprKind, PatKind};
@@ -25,9 +25,9 @@ pub(super) fn check<'tcx>(
if let PatKind::Binding(_, arg_id, _, _) = strip_pat_refs(param.pat).kind;
if let ExprKind::Binary(ref op, l, r) = body.value.kind;
if op.node == BinOpKind::Eq;
- if match_type(cx,
+ if is_type_diagnostic_item(cx,
cx.typeck_results().expr_ty(filter_recv).peel_refs(),
- &paths::SLICE_ITER);
+ sym::SliceIter);
let operand_is_arg = |expr| {
let expr = peel_ref_operators(cx, peel_blocks(expr));
path_to_local_id(expr, arg_id)
diff --git a/src/tools/clippy/clippy_lints/src/methods/bytes_nth.rs b/src/tools/clippy/clippy_lints/src/methods/bytes_nth.rs
index c5fc145b2..baafb7030 100644
--- a/src/tools/clippy/clippy_lints/src/methods/bytes_nth.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/bytes_nth.rs
@@ -24,8 +24,9 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, recv: &'tcx E
let n = snippet_with_applicability(cx, n_arg.span, "..", &mut applicability);
if let Some(parent) = clippy_utils::get_parent_expr(cx, expr)
- && let Some((name, _, _, _, _)) = method_call(parent)
- && name == "unwrap" {
+ && let Some((name, _, _, _, _)) = method_call(parent)
+ && name == "unwrap"
+ {
span_lint_and_sugg(
cx,
BYTES_NTH,
@@ -33,7 +34,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, recv: &'tcx E
&format!("called `.bytes().nth().unwrap()` on a `{caller_type}`"),
"try",
format!("{receiver}.as_bytes()[{n}]",),
- applicability
+ applicability,
);
} else {
span_lint_and_sugg(
@@ -42,8 +43,8 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, recv: &'tcx E
expr.span,
&format!("called `.bytes().nth()` on a `{caller_type}`"),
"try",
- format!("{receiver}.as_bytes().get({n}).copied()"),
- applicability
+ format!("{receiver}.as_bytes().get({n}).copied()"),
+ applicability,
);
};
}
diff --git a/src/tools/clippy/clippy_lints/src/methods/clone_on_ref_ptr.rs b/src/tools/clippy/clippy_lints/src/methods/clone_on_ref_ptr.rs
index ddf3c9f27..926bd06ba 100644
--- a/src/tools/clippy/clippy_lints/src/methods/clone_on_ref_ptr.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/clone_on_ref_ptr.rs
@@ -1,7 +1,5 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::paths;
use clippy_utils::source::snippet_with_context;
-use clippy_utils::ty::{is_type_diagnostic_item, match_type};
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::LateContext;
@@ -22,15 +20,14 @@ pub(super) fn check(
}
let obj_ty = cx.typeck_results().expr_ty(receiver).peel_refs();
- if let ty::Adt(_, subst) = obj_ty.kind() {
- let caller_type = if is_type_diagnostic_item(cx, obj_ty, sym::Rc) {
- "Rc"
- } else if is_type_diagnostic_item(cx, obj_ty, sym::Arc) {
- "Arc"
- } else if match_type(cx, obj_ty, &paths::WEAK_RC) || match_type(cx, obj_ty, &paths::WEAK_ARC) {
- "Weak"
- } else {
- return;
+ if let ty::Adt(adt, subst) = obj_ty.kind()
+ && let Some(name) = cx.tcx.get_diagnostic_name(adt.did())
+ {
+ let caller_type = match name {
+ sym::Rc => "Rc",
+ sym::Arc => "Arc",
+ sym::RcWeak | sym::ArcWeak => "Weak",
+ _ => return,
};
// Sometimes unnecessary ::<_> after Rc/Arc/Weak
diff --git a/src/tools/clippy/clippy_lints/src/methods/cloned_instead_of_copied.rs b/src/tools/clippy/clippy_lints/src/methods/cloned_instead_of_copied.rs
index 4e6ec61f6..fcafa1622 100644
--- a/src/tools/clippy/clippy_lints/src/methods/cloned_instead_of_copied.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/cloned_instead_of_copied.rs
@@ -1,6 +1,6 @@
+use clippy_config::msrvs::{self, Msrv};
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::is_trait_method;
-use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::ty::{get_iterator_item_ty, is_copy};
use rustc_errors::Applicability;
use rustc_hir::Expr;
diff --git a/src/tools/clippy/clippy_lints/src/methods/err_expect.rs b/src/tools/clippy/clippy_lints/src/methods/err_expect.rs
index 3d82441c0..a8d4dd5e4 100644
--- a/src/tools/clippy/clippy_lints/src/methods/err_expect.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/err_expect.rs
@@ -1,6 +1,6 @@
use super::ERR_EXPECT;
+use clippy_config::msrvs::{self, Msrv};
use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::ty::{has_debug_impl, is_type_diagnostic_item};
use rustc_errors::Applicability;
use rustc_lint::LateContext;
diff --git a/src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs b/src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs
index 40e487bf6..a49970b53 100644
--- a/src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs
@@ -6,7 +6,7 @@ use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::LateContext;
use rustc_middle::ty;
-use rustc_span::source_map::Span;
+use rustc_span::Span;
use rustc_span::symbol::sym;
use std::borrow::Cow;
diff --git a/src/tools/clippy/clippy_lints/src/methods/filetype_is_file.rs b/src/tools/clippy/clippy_lints/src/methods/filetype_is_file.rs
index 3fef53739..7818be811 100644
--- a/src/tools/clippy/clippy_lints/src/methods/filetype_is_file.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/filetype_is_file.rs
@@ -4,8 +4,7 @@ use clippy_utils::ty::is_type_diagnostic_item;
use if_chain::if_chain;
use rustc_hir as hir;
use rustc_lint::LateContext;
-use rustc_span::source_map::Span;
-use rustc_span::sym;
+use rustc_span::{sym, Span};
use super::FILETYPE_IS_FILE;
diff --git a/src/tools/clippy/clippy_lints/src/methods/filter_map.rs b/src/tools/clippy/clippy_lints/src/methods/filter_map.rs
index c9eaa185a..5bb8c7a6b 100644
--- a/src/tools/clippy/clippy_lints/src/methods/filter_map.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/filter_map.rs
@@ -11,7 +11,7 @@ use rustc_hir::def::Res;
use rustc_hir::{Closure, Expr, ExprKind, PatKind, PathSegment, QPath, UnOp};
use rustc_lint::LateContext;
use rustc_middle::ty::adjustment::Adjust;
-use rustc_span::source_map::Span;
+use rustc_span::Span;
use rustc_span::symbol::{sym, Ident, Symbol};
use std::borrow::Cow;
@@ -159,7 +159,7 @@ impl<'tcx> OffendingFilterExpr<'tcx> {
OffendingFilterExpr::IsSome { .. } => CalledMethod::OptionIsSome,
OffendingFilterExpr::IsOk { .. } => CalledMethod::ResultIsOk,
OffendingFilterExpr::Matches { .. } => unreachable!("only IsSome and IsOk can get here"),
- }
+ },
})
} else {
None
@@ -189,7 +189,8 @@ impl<'tcx> OffendingFilterExpr<'tcx> {
// scrutinee variant_span variant_ident else_
let (scrutinee, else_, variant_ident, variant_span) =
match higher::IfLetOrMatch::parse(cx, map_body.value) {
- // For `if let` we want to check that the variant matching arm references the local created by its pattern
+ // For `if let` we want to check that the variant matching arm references the local created by
+ // its pattern
Some(higher::IfLetOrMatch::IfLet(sc, pat, then, Some(else_)))
if let Some((ident, span)) = expr_uses_local(pat, then) =>
{
@@ -211,7 +212,10 @@ impl<'tcx> OffendingFilterExpr<'tcx> {
&& let Some(mac) = root_macro_call(else_.peel_blocks().span)
&& (is_panic(cx, mac.def_id) || cx.tcx.opt_item_name(mac.def_id) == Some(sym::unreachable))
{
- Some(CheckResult::PatternMatching { variant_span, variant_ident })
+ Some(CheckResult::PatternMatching {
+ variant_span,
+ variant_ident,
+ })
} else {
None
}
@@ -228,18 +232,20 @@ impl<'tcx> OffendingFilterExpr<'tcx> {
// .filter(|x| effect(x).is_some()).map(|x| effect(x).unwrap())
// vs.
// .filter_map(|x| effect(x))
- //
+ //
// the latter only calls `effect` once
let side_effect_expr_span = receiver.can_have_side_effects().then_some(receiver.span);
- if cx.tcx.is_diagnostic_item(sym::Option, recv_ty.did())
- && path.ident.name == sym!(is_some)
- {
- Some(Self::IsSome { receiver, side_effect_expr_span })
- } else if cx.tcx.is_diagnostic_item(sym::Result, recv_ty.did())
- && path.ident.name == sym!(is_ok)
- {
- Some(Self::IsOk { receiver, side_effect_expr_span })
+ if cx.tcx.is_diagnostic_item(sym::Option, recv_ty.did()) && path.ident.name == sym!(is_some) {
+ Some(Self::IsSome {
+ receiver,
+ side_effect_expr_span,
+ })
+ } else if cx.tcx.is_diagnostic_item(sym::Result, recv_ty.did()) && path.ident.name == sym!(is_ok) {
+ Some(Self::IsOk {
+ receiver,
+ side_effect_expr_span,
+ })
} else {
None
}
diff --git a/src/tools/clippy/clippy_lints/src/methods/filter_map_bool_then.rs b/src/tools/clippy/clippy_lints/src/methods/filter_map_bool_then.rs
index 336572549..9950c4428 100644
--- a/src/tools/clippy/clippy_lints/src/methods/filter_map_bool_then.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/filter_map_bool_then.rs
@@ -54,7 +54,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, arg: &
"use `filter` then `map` instead",
format!(
"filter(|&{param_snippet}| {derefs}{filter}).map(|{param_snippet}| {map})",
- derefs="*".repeat(needed_derefs)
+ derefs = "*".repeat(needed_derefs)
),
Applicability::MachineApplicable,
);
diff --git a/src/tools/clippy/clippy_lints/src/methods/filter_map_identity.rs b/src/tools/clippy/clippy_lints/src/methods/filter_map_identity.rs
index 3337b250c..8291c373f 100644
--- a/src/tools/clippy/clippy_lints/src/methods/filter_map_identity.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/filter_map_identity.rs
@@ -1,15 +1,14 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::{is_expr_identity_function, is_trait_method};
+use clippy_utils::{is_expr_untyped_identity_function, is_trait_method};
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::LateContext;
-use rustc_span::source_map::Span;
-use rustc_span::sym;
+use rustc_span::{sym, Span};
use super::FILTER_MAP_IDENTITY;
pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, filter_map_arg: &hir::Expr<'_>, filter_map_span: Span) {
- if is_trait_method(cx, expr, sym::Iterator) && is_expr_identity_function(cx, filter_map_arg) {
+ if is_trait_method(cx, expr, sym::Iterator) && is_expr_untyped_identity_function(cx, filter_map_arg) {
span_lint_and_sugg(
cx,
FILTER_MAP_IDENTITY,
diff --git a/src/tools/clippy/clippy_lints/src/methods/filter_map_next.rs b/src/tools/clippy/clippy_lints/src/methods/filter_map_next.rs
index 3f89e5931..f94fe2218 100644
--- a/src/tools/clippy/clippy_lints/src/methods/filter_map_next.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/filter_map_next.rs
@@ -1,6 +1,6 @@
+use clippy_config::msrvs::{self, Msrv};
use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
use clippy_utils::is_trait_method;
-use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::source::snippet;
use rustc_errors::Applicability;
use rustc_hir as hir;
diff --git a/src/tools/clippy/clippy_lints/src/methods/flat_map_identity.rs b/src/tools/clippy/clippy_lints/src/methods/flat_map_identity.rs
index 84a21de0a..651ea34f9 100644
--- a/src/tools/clippy/clippy_lints/src/methods/flat_map_identity.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/flat_map_identity.rs
@@ -1,10 +1,9 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::{is_expr_identity_function, is_trait_method};
+use clippy_utils::{is_expr_untyped_identity_function, is_trait_method};
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::LateContext;
-use rustc_span::source_map::Span;
-use rustc_span::sym;
+use rustc_span::{sym, Span};
use super::FLAT_MAP_IDENTITY;
@@ -15,7 +14,7 @@ pub(super) fn check<'tcx>(
flat_map_arg: &'tcx hir::Expr<'_>,
flat_map_span: Span,
) {
- if is_trait_method(cx, expr, sym::Iterator) && is_expr_identity_function(cx, flat_map_arg) {
+ if is_trait_method(cx, expr, sym::Iterator) && is_expr_untyped_identity_function(cx, flat_map_arg) {
span_lint_and_sugg(
cx,
FLAT_MAP_IDENTITY,
diff --git a/src/tools/clippy/clippy_lints/src/methods/flat_map_option.rs b/src/tools/clippy/clippy_lints/src/methods/flat_map_option.rs
index 172c397fb..0a4a381b8 100644
--- a/src/tools/clippy/clippy_lints/src/methods/flat_map_option.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/flat_map_option.rs
@@ -4,8 +4,7 @@ use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::LateContext;
use rustc_middle::ty;
-use rustc_span::source_map::Span;
-use rustc_span::sym;
+use rustc_span::{sym, Span};
use super::FLAT_MAP_OPTION;
use clippy_utils::ty::is_type_diagnostic_item;
diff --git a/src/tools/clippy/clippy_lints/src/methods/format_collect.rs b/src/tools/clippy/clippy_lints/src/methods/format_collect.rs
index 1f8863f85..3e5162ef4 100644
--- a/src/tools/clippy/clippy_lints/src/methods/format_collect.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/format_collect.rs
@@ -24,10 +24,16 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, map_arg: &Expr<'_>, m
&& let Some(mac) = root_macro_call_first_node(cx, value)
&& is_format_macro(cx, mac.def_id)
{
- span_lint_and_then(cx, FORMAT_COLLECT, expr.span, "use of `format!` to build up a string from an iterator", |diag| {
- diag.span_help(map_span, "call `fold` instead")
- .span_help(value.span.source_callsite(), "... and use the `write!` macro here")
- .note("this can be written more efficiently by appending to a `String` directly");
- });
+ span_lint_and_then(
+ cx,
+ FORMAT_COLLECT,
+ expr.span,
+ "use of `format!` to build up a string from an iterator",
+ |diag| {
+ diag.span_help(map_span, "call `fold` instead")
+ .span_help(value.span.source_callsite(), "... and use the `write!` macro here")
+ .note("this can be written more efficiently by appending to a `String` directly");
+ },
+ );
}
}
diff --git a/src/tools/clippy/clippy_lints/src/methods/from_iter_instead_of_collect.rs b/src/tools/clippy/clippy_lints/src/methods/from_iter_instead_of_collect.rs
index 66dfce368..4040d3a5f 100644
--- a/src/tools/clippy/clippy_lints/src/methods/from_iter_instead_of_collect.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/from_iter_instead_of_collect.rs
@@ -1,7 +1,7 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_opt;
use clippy_utils::ty::implements_trait;
-use clippy_utils::{is_expr_path_def_path, paths, sugg};
+use clippy_utils::{is_path_diagnostic_item, sugg};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir as hir;
@@ -13,7 +13,7 @@ use super::FROM_ITER_INSTEAD_OF_COLLECT;
pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>], func: &hir::Expr<'_>) {
if_chain! {
- if is_expr_path_def_path(cx, func, &paths::FROM_ITERATOR_METHOD);
+ if is_path_diagnostic_item(cx, func, sym::from_iter_fn);
let ty = cx.typeck_results().expr_ty(expr);
let arg_ty = cx.typeck_results().expr_ty(&args[0]);
if let Some(iter_id) = cx.tcx.get_diagnostic_item(sym::Iterator);
diff --git a/src/tools/clippy/clippy_lints/src/methods/get_first.rs b/src/tools/clippy/clippy_lints/src/methods/get_first.rs
index ee063adac..2e1dd3ec6 100644
--- a/src/tools/clippy/clippy_lints/src/methods/get_first.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/get_first.rs
@@ -1,12 +1,13 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::is_slice_of_primitives;
use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::ty::is_type_diagnostic_item;
use if_chain::if_chain;
use rustc_ast::LitKind;
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::LateContext;
use rustc_span::source_map::Spanned;
+use rustc_span::sym;
use super::GET_FIRST;
@@ -19,21 +20,34 @@ pub(super) fn check<'tcx>(
if_chain! {
if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
if let Some(impl_id) = cx.tcx.impl_of_method(method_id);
- if cx.tcx.type_of(impl_id).instantiate_identity().is_slice();
- if let Some(_) = is_slice_of_primitives(cx, recv);
+ let identity = cx.tcx.type_of(impl_id).instantiate_identity();
if let hir::ExprKind::Lit(Spanned { node: LitKind::Int(0, _), .. }) = arg.kind;
then {
- let mut app = Applicability::MachineApplicable;
- let slice_name = snippet_with_applicability(cx, recv.span, "..", &mut app);
- span_lint_and_sugg(
- cx,
- GET_FIRST,
- expr.span,
- &format!("accessing first element with `{slice_name}.get(0)`"),
- "try",
- format!("{slice_name}.first()"),
- app,
- );
+ if identity.is_slice() {
+ let mut app = Applicability::MachineApplicable;
+ let slice_name = snippet_with_applicability(cx, recv.span, "..", &mut app);
+ span_lint_and_sugg(
+ cx,
+ GET_FIRST,
+ expr.span,
+ &format!("accessing first element with `{slice_name}.get(0)`"),
+ "try",
+ format!("{slice_name}.first()"),
+ app,
+ );
+ } else if is_type_diagnostic_item(cx, identity, sym::VecDeque){
+ let mut app = Applicability::MachineApplicable;
+ let slice_name = snippet_with_applicability(cx, recv.span, "..", &mut app);
+ span_lint_and_sugg(
+ cx,
+ GET_FIRST,
+ expr.span,
+ &format!("accessing first element with `{slice_name}.get(0)`"),
+ "try",
+ format!("{slice_name}.front()"),
+ app,
+ );
+ }
}
}
}
diff --git a/src/tools/clippy/clippy_lints/src/methods/get_unwrap.rs b/src/tools/clippy/clippy_lints/src/methods/get_unwrap.rs
index a8f090d1d..afdcb3b65 100644
--- a/src/tools/clippy/clippy_lints/src/methods/get_unwrap.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/get_unwrap.rs
@@ -43,9 +43,9 @@ pub(super) fn check<'tcx>(
// by not requiring an explicit reference
let needs_ref = if let Some(parent) = get_parent_expr(cx, expr)
&& let hir::ExprKind::Unary(hir::UnOp::Deref, _)
- | hir::ExprKind::MethodCall(..)
- | hir::ExprKind::Field(..)
- | hir::ExprKind::Index(..) = parent.kind
+ | hir::ExprKind::MethodCall(..)
+ | hir::ExprKind::Field(..)
+ | hir::ExprKind::Index(..) = parent.kind
{
if let hir::ExprKind::Unary(hir::UnOp::Deref, _) = parent.kind {
// if the user explicitly dereferences the result, we can adjust
diff --git a/src/tools/clippy/clippy_lints/src/methods/inefficient_to_string.rs b/src/tools/clippy/clippy_lints/src/methods/inefficient_to_string.rs
index 631741d92..6686d42c9 100644
--- a/src/tools/clippy/clippy_lints/src/methods/inefficient_to_string.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/inefficient_to_string.rs
@@ -1,7 +1,6 @@
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::ty::{is_type_lang_item, walk_ptrs_ty_depth};
-use clippy_utils::{match_def_path, paths};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir as hir;
@@ -22,7 +21,7 @@ pub fn check(
if_chain! {
if args.is_empty() && method_name == sym::to_string;
if let Some(to_string_meth_did) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
- if match_def_path(cx, to_string_meth_did, &paths::TO_STRING_METHOD);
+ if cx.tcx.is_diagnostic_item(sym::to_string_method, to_string_meth_did);
if let Some(args) = cx.typeck_results().node_args_opt(expr.hir_id);
let arg_ty = cx.typeck_results().expr_ty_adjusted(receiver);
let self_ty = args.type_at(0);
diff --git a/src/tools/clippy/clippy_lints/src/methods/inspect_for_each.rs b/src/tools/clippy/clippy_lints/src/methods/inspect_for_each.rs
index 23cc192c3..ad4b6fa13 100644
--- a/src/tools/clippy/clippy_lints/src/methods/inspect_for_each.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/inspect_for_each.rs
@@ -2,8 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::is_trait_method;
use rustc_hir as hir;
use rustc_lint::LateContext;
-use rustc_span::source_map::Span;
-use rustc_span::sym;
+use rustc_span::{sym, Span};
use super::INSPECT_FOR_EACH;
diff --git a/src/tools/clippy/clippy_lints/src/methods/into_iter_on_ref.rs b/src/tools/clippy/clippy_lints/src/methods/into_iter_on_ref.rs
index 8adf9e370..bbd964c10 100644
--- a/src/tools/clippy/clippy_lints/src/methods/into_iter_on_ref.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/into_iter_on_ref.rs
@@ -6,7 +6,7 @@ use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::LateContext;
use rustc_middle::ty::{self, Ty};
-use rustc_span::source_map::Span;
+use rustc_span::Span;
use rustc_span::symbol::{sym, Symbol};
use super::INTO_ITER_ON_REF;
diff --git a/src/tools/clippy/clippy_lints/src/methods/is_digit_ascii_radix.rs b/src/tools/clippy/clippy_lints/src/methods/is_digit_ascii_radix.rs
index 120f3d5f4..e96395096 100644
--- a/src/tools/clippy/clippy_lints/src/methods/is_digit_ascii_radix.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/is_digit_ascii_radix.rs
@@ -1,9 +1,9 @@
//! Lint for `c.is_digit(10)`
use super::IS_DIGIT_ASCII_RADIX;
+use clippy_config::msrvs::{self, Msrv};
use clippy_utils::consts::{constant_full_int, FullInt};
use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::source::snippet_with_applicability;
use rustc_errors::Applicability;
use rustc_hir::Expr;
diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_kv_map.rs b/src/tools/clippy/clippy_lints/src/methods/iter_kv_map.rs
index 674d34517..625325d4c 100644
--- a/src/tools/clippy/clippy_lints/src/methods/iter_kv_map.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/iter_kv_map.rs
@@ -3,9 +3,8 @@
use super::ITER_KV_MAP;
use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_sugg, span_lint_and_then};
use clippy_utils::source::{snippet, snippet_with_applicability};
-use clippy_utils::sugg;
use clippy_utils::ty::is_type_diagnostic_item;
-use clippy_utils::visitors::is_local_used;
+use clippy_utils::{pat_is_wild, sugg};
use rustc_hir::{BindingAnnotation, Body, BorrowKind, ByRef, Expr, ExprKind, Mutability, Pat, PatKind};
use rustc_lint::{LateContext, LintContext};
use rustc_middle::ty;
@@ -26,7 +25,7 @@ pub(super) fn check<'tcx>(
if_chain! {
if !expr.span.from_expansion();
if let ExprKind::Closure(c) = m_arg.kind;
- if let Body {params: [p], value: body_expr, generator_kind: _ } = cx.tcx.hir().body(c.body);
+ if let Body {params: [p], value: body_expr, coroutine_kind: _ } = cx.tcx.hir().body(c.body);
if let PatKind::Tuple([key_pat, val_pat], _) = p.pat.kind;
let (replacement_kind, annotation, bound_ident) = match (&key_pat.kind, &val_pat.kind) {
@@ -84,13 +83,3 @@ pub(super) fn check<'tcx>(
}
}
}
-
-/// Returns `true` if the pattern is a `PatWild`, or is an ident prefixed with `_`
-/// that is not locally used.
-fn pat_is_wild<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx PatKind<'_>, body: &'tcx Expr<'_>) -> bool {
- match *pat {
- PatKind::Wild => true,
- PatKind::Binding(_, id, ident, None) if ident.as_str().starts_with('_') => !is_local_used(cx, body, id),
- _ => false,
- }
-}
diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_out_of_bounds.rs b/src/tools/clippy/clippy_lints/src/methods/iter_out_of_bounds.rs
index 79c6d6325..29e69b111 100644
--- a/src/tools/clippy/clippy_lints/src/methods/iter_out_of_bounds.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/iter_out_of_bounds.rs
@@ -1,6 +1,6 @@
use clippy_utils::diagnostics::span_lint_and_note;
use clippy_utils::higher::VecArgs;
-use clippy_utils::{expr_or_init, is_trait_method, match_def_path, paths};
+use clippy_utils::{expr_or_init, is_trait_method};
use rustc_ast::LitKind;
use rustc_hir::{Expr, ExprKind};
use rustc_lint::LateContext;
@@ -26,14 +26,14 @@ fn get_iterator_length<'tcx>(cx: &LateContext<'tcx>, iter: &'tcx Expr<'tcx>) ->
};
let did = adt.did();
- if match_def_path(cx, did, &paths::ARRAY_INTO_ITER) {
+ if cx.tcx.is_diagnostic_item(sym::ArrayIntoIter, did) {
// For array::IntoIter<T, const N: usize>, the length is the second generic
// parameter.
substs
.const_at(1)
.try_eval_target_usize(cx.tcx, cx.param_env)
.map(u128::from)
- } else if match_def_path(cx, did, &paths::SLICE_ITER)
+ } else if cx.tcx.is_diagnostic_item(sym::SliceIter, did)
&& let ExprKind::MethodCall(_, recv, ..) = iter.kind
{
if let ty::Array(_, len) = cx.typeck_results().expr_ty(recv).peel_refs().kind() {
@@ -47,11 +47,11 @@ fn get_iterator_length<'tcx>(cx: &LateContext<'tcx>, iter: &'tcx Expr<'tcx>) ->
} else {
None
}
- } else if match_def_path(cx, did, &paths::ITER_EMPTY) {
+ } else if cx.tcx.is_diagnostic_item(sym::IterEmpty, did) {
Some(0)
- } else if match_def_path(cx, did, &paths::ITER_ONCE) {
+ } else if cx.tcx.is_diagnostic_item(sym::IterOnce, did) {
Some(1)
- } else {
+ } else {
None
}
}
diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_overeager_cloned.rs b/src/tools/clippy/clippy_lints/src/methods/iter_overeager_cloned.rs
index a49dd98db..eac6df054 100644
--- a/src/tools/clippy/clippy_lints/src/methods/iter_overeager_cloned.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/iter_overeager_cloned.rs
@@ -60,9 +60,15 @@ pub(super) fn check<'tcx>(
}
if let Op::NeedlessMove(_, expr) = op {
- let rustc_hir::ExprKind::Closure(closure) = expr.kind else { return } ;
- let body @ Body { params: [p], .. } = cx.tcx.hir().body(closure.body) else { return };
- let mut delegate = MoveDelegate {used_move : HirIdSet::default()};
+ let rustc_hir::ExprKind::Closure(closure) = expr.kind else {
+ return;
+ };
+ let body @ Body { params: [p], .. } = cx.tcx.hir().body(closure.body) else {
+ return;
+ };
+ let mut delegate = MoveDelegate {
+ used_move: HirIdSet::default(),
+ };
let infcx = cx.tcx.infer_ctxt().build();
ExprUseVisitor::new(
@@ -77,7 +83,7 @@ pub(super) fn check<'tcx>(
let mut to_be_discarded = false;
p.pat.walk(|it| {
- if delegate.used_move.contains(&it.hir_id){
+ if delegate.used_move.contains(&it.hir_id) {
to_be_discarded = true;
return false;
}
@@ -87,8 +93,8 @@ pub(super) fn check<'tcx>(
| PatKind::Ref(_, Mutability::Mut) => {
to_be_discarded = true;
false
- }
- _ => { true }
+ },
+ _ => true,
}
});
@@ -99,46 +105,42 @@ pub(super) fn check<'tcx>(
let (lint, msg, trailing_clone) = match op {
Op::RmCloned | Op::NeedlessMove(_, _) => (REDUNDANT_CLONE, "unneeded cloning of iterator items", ""),
- Op::LaterCloned | Op::FixClosure(_, _) => (ITER_OVEREAGER_CLONED, "unnecessarily eager cloning of iterator items", ".cloned()"),
+ Op::LaterCloned | Op::FixClosure(_, _) => (
+ ITER_OVEREAGER_CLONED,
+ "unnecessarily eager cloning of iterator items",
+ ".cloned()",
+ ),
};
- span_lint_and_then(
- cx,
- lint,
- expr.span,
- msg,
- |diag| {
- match op {
- Op::RmCloned | Op::LaterCloned => {
- let method_span = expr.span.with_lo(cloned_call.span.hi());
- if let Some(mut snip) = snippet_opt(cx, method_span) {
- snip.push_str(trailing_clone);
- let replace_span = expr.span.with_lo(cloned_recv.span.hi());
- diag.span_suggestion(replace_span, "try", snip, Applicability::MachineApplicable);
- }
- }
- Op::FixClosure(name, predicate_expr) => {
- if let Some(predicate) = snippet_opt(cx, predicate_expr.span) {
- let new_closure = if let ExprKind::Closure(_) = predicate_expr.kind {
- predicate.replacen('|', "|&", 1)
- } else {
- format!("|&x| {predicate}(x)")
- };
- let snip = format!(".{name}({new_closure}).cloned()" );
- let replace_span = expr.span.with_lo(cloned_recv.span.hi());
- diag.span_suggestion(replace_span, "try", snip, Applicability::MachineApplicable);
- }
- }
- Op::NeedlessMove(_, _) => {
- let method_span = expr.span.with_lo(cloned_call.span.hi());
- if let Some(snip) = snippet_opt(cx, method_span) {
- let replace_span = expr.span.with_lo(cloned_recv.span.hi());
- diag.span_suggestion(replace_span, "try", snip, Applicability::MaybeIncorrect);
- }
- }
+ span_lint_and_then(cx, lint, expr.span, msg, |diag| match op {
+ Op::RmCloned | Op::LaterCloned => {
+ let method_span = expr.span.with_lo(cloned_call.span.hi());
+ if let Some(mut snip) = snippet_opt(cx, method_span) {
+ snip.push_str(trailing_clone);
+ let replace_span = expr.span.with_lo(cloned_recv.span.hi());
+ diag.span_suggestion(replace_span, "try", snip, Applicability::MachineApplicable);
}
- }
- );
+ },
+ Op::FixClosure(name, predicate_expr) => {
+ if let Some(predicate) = snippet_opt(cx, predicate_expr.span) {
+ let new_closure = if let ExprKind::Closure(_) = predicate_expr.kind {
+ predicate.replacen('|', "|&", 1)
+ } else {
+ format!("|&x| {predicate}(x)")
+ };
+ let snip = format!(".{name}({new_closure}).cloned()");
+ let replace_span = expr.span.with_lo(cloned_recv.span.hi());
+ diag.span_suggestion(replace_span, "try", snip, Applicability::MachineApplicable);
+ }
+ },
+ Op::NeedlessMove(_, _) => {
+ let method_span = expr.span.with_lo(cloned_call.span.hi());
+ if let Some(snip) = snippet_opt(cx, method_span) {
+ let replace_span = expr.span.with_lo(cloned_recv.span.hi());
+ diag.span_suggestion(replace_span, "try", snip, Applicability::MaybeIncorrect);
+ }
+ },
+ });
}
}
diff --git a/src/tools/clippy/clippy_lints/src/methods/manual_try_fold.rs b/src/tools/clippy/clippy_lints/src/methods/manual_try_fold.rs
index dabed0aff..51145afda 100644
--- a/src/tools/clippy/clippy_lints/src/methods/manual_try_fold.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/manual_try_fold.rs
@@ -1,6 +1,6 @@
+use clippy_config::msrvs::{self, Msrv};
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::is_from_proc_macro;
-use clippy_utils::msrvs::{Msrv, ITERATOR_TRY_FOLD};
use clippy_utils::source::snippet_opt;
use clippy_utils::ty::implements_trait;
use rustc_errors::Applicability;
@@ -21,7 +21,7 @@ pub(super) fn check<'tcx>(
msrv: &Msrv,
) {
if !in_external_macro(cx.sess(), fold_span)
- && msrv.meets(ITERATOR_TRY_FOLD)
+ && msrv.meets(msrvs::ITERATOR_TRY_FOLD)
&& let init_ty = cx.typeck_results().expr_ty(init)
&& let Some(try_trait) = cx.tcx.lang_items().try_trait()
&& implements_trait(cx, init_ty, try_trait, &[])
@@ -44,7 +44,7 @@ pub(super) fn check<'tcx>(
fold_span,
"usage of `Iterator::fold` on a type that implements `Try`",
"use `try_fold` instead",
- format!("try_fold({init_snip}, {args_snip} ...)", ),
+ format!("try_fold({init_snip}, {args_snip} ...)",),
Applicability::HasPlaceholders,
);
}
diff --git a/src/tools/clippy/clippy_lints/src/methods/map_clone.rs b/src/tools/clippy/clippy_lints/src/methods/map_clone.rs
index 880efe60c..e0f8cb1b9 100644
--- a/src/tools/clippy/clippy_lints/src/methods/map_clone.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/map_clone.rs
@@ -1,5 +1,5 @@
+use clippy_config::msrvs::{self, Msrv};
use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::ty::{is_copy, is_type_diagnostic_item};
use clippy_utils::{is_diag_trait_item, peel_blocks};
diff --git a/src/tools/clippy/clippy_lints/src/methods/map_identity.rs b/src/tools/clippy/clippy_lints/src/methods/map_identity.rs
index 7be1ce483..bcfd0de8e 100644
--- a/src/tools/clippy/clippy_lints/src/methods/map_identity.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/map_identity.rs
@@ -1,11 +1,10 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::ty::is_type_diagnostic_item;
-use clippy_utils::{is_expr_identity_function, is_trait_method};
+use clippy_utils::{is_expr_untyped_identity_function, is_trait_method};
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::LateContext;
-use rustc_span::source_map::Span;
-use rustc_span::sym;
+use rustc_span::{sym, Span};
use super::MAP_IDENTITY;
@@ -23,7 +22,7 @@ pub(super) fn check(
if is_trait_method(cx, expr, sym::Iterator)
|| is_type_diagnostic_item(cx, caller_ty, sym::Result)
|| is_type_diagnostic_item(cx, caller_ty, sym::Option);
- if is_expr_identity_function(cx, map_arg);
+ if is_expr_untyped_identity_function(cx, map_arg);
if let Some(sugg_span) = expr.span.trim_start(caller.span);
then {
span_lint_and_sugg(
diff --git a/src/tools/clippy/clippy_lints/src/methods/map_unwrap_or.rs b/src/tools/clippy/clippy_lints/src/methods/map_unwrap_or.rs
index 5464e455d..cb81b3919 100644
--- a/src/tools/clippy/clippy_lints/src/methods/map_unwrap_or.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/map_unwrap_or.rs
@@ -1,5 +1,5 @@
+use clippy_config::msrvs::{self, Msrv};
use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
-use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::source::snippet;
use clippy_utils::ty::is_type_diagnostic_item;
use clippy_utils::usage::mutated_variables;
@@ -56,7 +56,7 @@ pub(super) fn check<'tcx>(
// lint, with note if neither arg is > 1 line and both map() and
// unwrap_or_else() have the same span
let multiline = map_snippet.lines().count() > 1 || unwrap_snippet.lines().count() > 1;
- let same_span = map_arg.span.ctxt() == unwrap_arg.span.ctxt();
+ let same_span = map_arg.span.eq_ctxt(unwrap_arg.span);
if same_span && !multiline {
let var_snippet = snippet(cx, recv.span, "..");
span_lint_and_sugg(
diff --git a/src/tools/clippy/clippy_lints/src/methods/mod.rs b/src/tools/clippy/clippy_lints/src/methods/mod.rs
index e7fcef9e9..a71a136eb 100644
--- a/src/tools/clippy/clippy_lints/src/methods/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/mod.rs
@@ -99,6 +99,7 @@ mod suspicious_to_owned;
mod type_id_on_box;
mod uninit_assumed_init;
mod unit_hash;
+mod unnecessary_fallible_conversions;
mod unnecessary_filter_map;
mod unnecessary_fold;
mod unnecessary_iter_cloned;
@@ -112,13 +113,14 @@ mod useless_asref;
mod utils;
mod vec_resize_to_zero;
mod verbose_file_reads;
+mod waker_clone_wake;
mod wrong_self_convention;
mod zst_offset;
use bind_instead_of_map::BindInsteadOfMap;
+use clippy_config::msrvs::{self, Msrv};
use clippy_utils::consts::{constant, Constant};
use clippy_utils::diagnostics::{span_lint, span_lint_and_help};
-use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::ty::{contains_ty_adt_constructor_opaque, implements_trait, is_copy, is_type_diagnostic_item};
use clippy_utils::{contains_return, is_bool, is_trait_method, iter_input_pats, peel_blocks, return_ty};
use if_chain::if_chain;
@@ -142,11 +144,11 @@ declare_clippy_lint! {
/// implements `Copy`.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// [1, 2, 3].iter().cloned();
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// [1, 2, 3].iter().copied();
/// ```
#[clippy::version = "1.53.0"]
@@ -165,14 +167,14 @@ declare_clippy_lint! {
/// with repetitive code.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let hello = "hesuo worpd"
/// .replace('s', "l")
/// .replace("u", "l")
/// .replace('p', "l");
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// let hello = "hesuo worpd".replace(['s', 'u', 'p'], "l");
/// ```
#[clippy::version = "1.65.0"]
@@ -194,14 +196,14 @@ declare_clippy_lint! {
/// A code that relies on that side-effect could fail.
///
/// ### Examples
- /// ```rust
+ /// ```no_run
/// # let vec = vec!["string".to_string()];
/// vec.iter().cloned().take(10);
/// vec.iter().cloned().last();
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # let vec = vec!["string".to_string()];
/// vec.iter().take(10).cloned();
/// vec.iter().last().cloned();
@@ -222,11 +224,11 @@ declare_clippy_lint! {
/// `Option` is used to produce 0 or 1 items.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let nums: Vec<i32> = ["1", "2", "whee!"].iter().flat_map(|x| x.parse().ok()).collect();
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// let nums: Vec<i32> = ["1", "2", "whee!"].iter().filter_map(|x| x.parse().ok()).collect();
/// ```
#[clippy::version = "1.53.0"]
@@ -254,7 +256,7 @@ declare_clippy_lint! {
/// where they may get displayed. Activate this lint to do just that.
///
/// ### Examples
- /// ```rust
+ /// ```no_run
/// # let option = Some(1);
/// # let result: Result<usize, ()> = Ok(1);
/// option.unwrap();
@@ -262,7 +264,7 @@ declare_clippy_lint! {
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # let option = Some(1);
/// # let result: Result<usize, ()> = Ok(1);
/// option.expect("more helpful message");
@@ -293,14 +295,14 @@ declare_clippy_lint! {
/// It is better to write the value directly without the indirection.
///
/// ### Examples
- /// ```rust
+ /// ```no_run
/// let val1 = Some(1).unwrap();
/// let val2 = Ok::<_, ()>(1).unwrap();
/// let val3 = Err::<(), _>(1).unwrap_err();
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// let val1 = 1;
/// let val2 = 1;
/// let val3 = 1;
@@ -363,7 +365,7 @@ declare_clippy_lint! {
/// them.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// struct X;
/// impl X {
/// fn add(&self, other: &X) -> X {
@@ -412,7 +414,7 @@ declare_clippy_lint! {
/// mutable reference to a `as_..` function.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # struct X;
/// impl X {
/// fn as_str(self) -> &'static str {
@@ -439,13 +441,13 @@ declare_clippy_lint! {
/// The error type needs to implement `Debug`
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # let x = Ok::<_, ()>(());
/// x.ok().expect("why did I do this again?");
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # let x = Ok::<_, ()>(());
/// x.expect("why did I do this again?");
/// ```
@@ -496,7 +498,7 @@ declare_clippy_lint! {
/// heuristic to try to identify such cases. However, the heuristic can produce false negatives.
///
/// ### Examples
- /// ```rust
+ /// ```no_run
/// # let x = Some(1);
/// # let mut map = std::collections::HashMap::<u64, String>::new();
/// x.unwrap_or(Default::default());
@@ -504,7 +506,7 @@ declare_clippy_lint! {
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # let x = Some(1);
/// # let mut map = std::collections::HashMap::<u64, String>::new();
/// x.unwrap_or_default();
@@ -529,7 +531,7 @@ declare_clippy_lint! {
/// The order of the arguments is not in execution order
///
/// ### Examples
- /// ```rust
+ /// ```no_run
/// # let option = Some(1);
/// # let result: Result<usize, ()> = Ok(1);
/// # fn some_function(foo: ()) -> usize { 1 }
@@ -539,7 +541,7 @@ declare_clippy_lint! {
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # let option = Some(1);
/// # let result: Result<usize, ()> = Ok(1);
/// # fn some_function(foo: ()) -> usize { 1 }
@@ -565,13 +567,13 @@ declare_clippy_lint! {
/// The order of the arguments is not in execution order.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # let opt = Some(1);
/// opt.map_or(None, |a| Some(a + 1));
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # let opt = Some(1);
/// opt.and_then(|a| Some(a + 1));
/// ```
@@ -590,13 +592,13 @@ declare_clippy_lint! {
/// `_.ok()`.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # let r: Result<u32, &str> = Ok(1);
/// assert_eq!(Some(1), r.map_or(None, Some));
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # let r: Result<u32, &str> = Ok(1);
/// assert_eq!(Some(1), r.ok());
/// ```
@@ -616,7 +618,7 @@ declare_clippy_lint! {
/// `_.map(|x| y)` or `_.map_err(|x| y)`.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # fn opt() -> Option<&'static str> { Some("42") }
/// # fn res() -> Result<&'static str, &'static str> { Ok("42") }
/// let _ = opt().and_then(|s| Some(s.len()));
@@ -626,7 +628,7 @@ declare_clippy_lint! {
///
/// The correct use would be:
///
- /// ```rust
+ /// ```no_run
/// # fn opt() -> Option<&'static str> { Some("42") }
/// # fn res() -> Result<&'static str, &'static str> { Ok("42") }
/// let _ = opt().map(|s| s.len());
@@ -648,13 +650,13 @@ declare_clippy_lint! {
/// `_.find(_)`.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # let vec = vec![1];
/// vec.iter().filter(|x| **x == 0).next();
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # let vec = vec![1];
/// vec.iter().find(|x| **x == 0);
/// ```
@@ -673,13 +675,13 @@ declare_clippy_lint! {
/// `_.find(!condition)`.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # let vec = vec![1];
/// vec.iter().skip_while(|x| **x == 0).next();
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # let vec = vec![1];
/// vec.iter().find(|x| **x != 0);
/// ```
@@ -698,7 +700,7 @@ declare_clippy_lint! {
/// `_.flat_map(_)` for `Iterator` or `_.and_then(_)` for `Option`
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let vec = vec![vec![1]];
/// let opt = Some(5);
///
@@ -707,7 +709,7 @@ declare_clippy_lint! {
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # let vec = vec![vec![1]];
/// # let opt = Some(5);
/// vec.iter().flat_map(|x| x.iter());
@@ -729,7 +731,7 @@ declare_clippy_lint! {
/// less performant.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # #![allow(unused)]
/// (0_i32..10)
/// .filter(|n| n.checked_add(1).is_some())
@@ -737,7 +739,7 @@ declare_clippy_lint! {
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # #[allow(unused)]
/// (0_i32..10).filter_map(|n| n.checked_add(1));
/// ```
@@ -757,14 +759,14 @@ declare_clippy_lint! {
/// less performant.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// (0_i32..10)
/// .find(|n| n.checked_add(1).is_some())
/// .map(|n| n.checked_add(1).unwrap());
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// (0_i32..10).find_map(|n| n.checked_add(1));
/// ```
#[clippy::version = "1.51.0"]
@@ -782,12 +784,12 @@ declare_clippy_lint! {
/// `_.find_map(_)`.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// (0..3).filter_map(|x| if x == 2 { Some(x) } else { None }).next();
/// ```
/// Can be written as
///
- /// ```rust
+ /// ```no_run
/// (0..3).find_map(|x| if x == 2 { Some(x) } else { None });
/// ```
#[clippy::version = "1.36.0"]
@@ -804,12 +806,12 @@ declare_clippy_lint! {
/// Readability, this can be written more concisely by using `flatten`.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # let iter = vec![vec![0]].into_iter();
/// iter.flat_map(|x| x);
/// ```
/// Can be written as
- /// ```rust
+ /// ```no_run
/// # let iter = vec![vec![0]].into_iter();
/// iter.flatten();
/// ```
@@ -830,7 +832,7 @@ declare_clippy_lint! {
/// * `!_.any(_)`, or `!_.contains(_)` for `is_none()`.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # #![allow(unused)]
/// let vec = vec![1];
/// vec.iter().find(|x| **x == 0).is_some();
@@ -839,7 +841,7 @@ declare_clippy_lint! {
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// let vec = vec![1];
/// vec.iter().any(|x| *x == 0);
///
@@ -862,13 +864,13 @@ declare_clippy_lint! {
/// `_.starts_with(_)`.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let name = "foo";
/// if name.chars().next() == Some('_') {};
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// let name = "foo";
/// if name.starts_with('_') {};
/// ```
@@ -897,13 +899,13 @@ declare_clippy_lint! {
/// actually expensive to call or not.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # let foo = Some(String::new());
/// foo.unwrap_or(String::from("empty"));
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # let foo = Some(String::new());
/// foo.unwrap_or_else(|| String::from("empty"));
/// ```
@@ -921,7 +923,7 @@ declare_clippy_lint! {
/// You should use `.unwrap_or(…)` instead for clarity.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # let fallback = "fallback";
/// // Result
/// # type Error = &'static str;
@@ -933,7 +935,7 @@ declare_clippy_lint! {
/// let value = option.or(Some(fallback)).unwrap();
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # let fallback = "fallback";
/// // Result
/// # let result: Result<&str, &str> = Err("error");
@@ -962,7 +964,7 @@ declare_clippy_lint! {
/// change the semantics of the program, but you shouldn't rely on that anyway.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # let foo = Some(String::new());
/// # let err_code = "418";
/// # let err_msg = "I'm a teapot";
@@ -975,7 +977,7 @@ declare_clippy_lint! {
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # let foo = Some(String::new());
/// # let err_code = "418";
/// # let err_msg = "I'm a teapot";
@@ -996,7 +998,7 @@ declare_clippy_lint! {
/// generics, not for using the `clone` method on a concrete type.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// 42u64.clone();
/// ```
#[clippy::version = "pre 1.29.0"]
@@ -1017,7 +1019,7 @@ declare_clippy_lint! {
/// data.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # use std::rc::Rc;
/// let x = Rc::new(1);
///
@@ -1025,7 +1027,7 @@ declare_clippy_lint! {
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # use std::rc::Rc;
/// # let x = Rc::new(1);
/// Rc::clone(&x);
@@ -1047,7 +1049,7 @@ declare_clippy_lint! {
/// facilities.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// // Generic implementation for `T: Display` is used (slow)
/// ["foo", "bar"].iter().map(|s| s.to_string());
///
@@ -1070,7 +1072,7 @@ declare_clippy_lint! {
///
/// ### Example
/// In an impl block:
- /// ```rust
+ /// ```no_run
/// # struct Foo;
/// # struct NotAFoo;
/// impl Foo {
@@ -1080,7 +1082,7 @@ declare_clippy_lint! {
/// }
/// ```
///
- /// ```rust
+ /// ```no_run
/// # struct Foo;
/// struct Bar(Foo);
/// impl Foo {
@@ -1091,7 +1093,7 @@ declare_clippy_lint! {
/// }
/// ```
///
- /// ```rust
+ /// ```no_run
/// # struct Foo;
/// # struct FooError;
/// impl Foo {
@@ -1103,14 +1105,14 @@ declare_clippy_lint! {
/// ```
///
/// Or in a trait definition:
- /// ```rust
+ /// ```no_run
/// pub trait Trait {
/// // Bad. The type name must contain `Self`
/// fn new();
/// }
/// ```
///
- /// ```rust
+ /// ```no_run
/// pub trait Trait {
/// // Good. Return type contains `Self`
/// fn new() -> Self;
@@ -1178,11 +1180,11 @@ declare_clippy_lint! {
/// automatically does this without suspicious-looking `unwrap` calls.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let _ = std::iter::empty::<Option<i32>>().filter(Option::is_some).map(Option::unwrap);
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// let _ = std::iter::empty::<Option<i32>>().flatten();
/// ```
#[clippy::version = "1.53.0"]
@@ -1201,7 +1203,7 @@ declare_clippy_lint! {
/// but is more readable.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # use std::collections::HashSet;
/// # let mut s = HashSet::new();
/// # s.insert(1);
@@ -1209,7 +1211,7 @@ declare_clippy_lint! {
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # use std::collections::HashSet;
/// # let mut s = HashSet::new();
/// # s.insert(1);
@@ -1231,13 +1233,13 @@ declare_clippy_lint! {
/// readable.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let some_vec = vec![0, 1, 2, 3];
/// let bad_vec = some_vec.iter().nth(3);
/// let bad_slice = &some_vec[..].iter().nth(3);
/// ```
/// The correct use would be:
- /// ```rust
+ /// ```no_run
/// let some_vec = vec![0, 1, 2, 3];
/// let bad_vec = some_vec.get(3);
/// let bad_slice = &some_vec[..].get(3);
@@ -1256,13 +1258,13 @@ declare_clippy_lint! {
/// `.nth(x)` is cleaner
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let some_vec = vec![0, 1, 2, 3];
/// let bad_vec = some_vec.iter().skip(3).next();
/// let bad_slice = &some_vec[..].iter().skip(3).next();
/// ```
/// The correct use would be:
- /// ```rust
+ /// ```no_run
/// let some_vec = vec![0, 1, 2, 3];
/// let bad_vec = some_vec.iter().nth(3);
/// let bad_slice = &some_vec[..].iter().nth(3);
@@ -1281,13 +1283,13 @@ declare_clippy_lint! {
/// `.into_iter()` is simpler with better performance.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # use std::collections::HashSet;
/// let mut foo = vec![0, 1, 2, 3];
/// let bar: HashSet<usize> = foo.drain(..).collect();
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # use std::collections::HashSet;
/// let foo = vec![0, 1, 2, 3];
/// let bar: HashSet<usize> = foo.into_iter().collect();
@@ -1315,13 +1317,13 @@ declare_clippy_lint! {
/// `x.get(index).unwrap()` instead of `x[index]`.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let x = vec![2, 3, 5];
/// let last_element = x.get(x.len() - 1);
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// let x = vec![2, 3, 5];
/// let last_element = x.last();
/// ```
@@ -1351,13 +1353,13 @@ declare_clippy_lint! {
/// trait.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let mut some_vec = vec![0, 1, 2, 3];
/// let last = some_vec.get(3).unwrap();
/// *some_vec.get_mut(0).unwrap() = 1;
/// ```
/// The correct use would be:
- /// ```rust
+ /// ```no_run
/// let mut some_vec = vec![0, 1, 2, 3];
/// let last = some_vec[3];
/// some_vec[0] = 1;
@@ -1376,7 +1378,7 @@ declare_clippy_lint! {
/// Using `append` instead of `extend` is more concise and faster
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let mut a = vec![1, 2, 3];
/// let mut b = vec![4, 5, 6];
///
@@ -1384,7 +1386,7 @@ declare_clippy_lint! {
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// let mut a = vec![1, 2, 3];
/// let mut b = vec![4, 5, 6];
///
@@ -1405,7 +1407,7 @@ declare_clippy_lint! {
/// `.push_str(s)` is clearer
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let abc = "abc";
/// let def = String::from("def");
/// let mut s = String::new();
@@ -1413,7 +1415,7 @@ declare_clippy_lint! {
/// s.extend(def.chars());
/// ```
/// The correct use would be:
- /// ```rust
+ /// ```no_run
/// let abc = "abc";
/// let def = String::from("def");
/// let mut s = String::new();
@@ -1435,12 +1437,12 @@ declare_clippy_lint! {
/// `.to_vec()` is clearer
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let s = [1, 2, 3, 4, 5];
/// let s2: Vec<isize> = s[..].iter().cloned().collect();
/// ```
/// The better use would be:
- /// ```rust
+ /// ```no_run
/// let s = [1, 2, 3, 4, 5];
/// let s2: Vec<isize> = s.to_vec();
/// ```
@@ -1460,13 +1462,13 @@ declare_clippy_lint! {
/// `_.ends_with(_)`.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # let name = "_";
/// name.chars().last() == Some('_') || name.chars().next_back() == Some('-');
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # let name = "_";
/// name.ends_with('_') || name.ends_with('-');
/// ```
@@ -1485,13 +1487,13 @@ declare_clippy_lint! {
/// The call is unnecessary.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # fn do_stuff(x: &[i32]) {}
/// let x: &[i32] = &[1, 2, 3, 4, 5];
/// do_stuff(x.as_ref());
/// ```
/// The correct use would be:
- /// ```rust
+ /// ```no_run
/// # fn do_stuff(x: &[i32]) {}
/// let x: &[i32] = &[1, 2, 3, 4, 5];
/// do_stuff(x);
@@ -1512,13 +1514,13 @@ declare_clippy_lint! {
/// Readability.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # #[allow(unused)]
/// (0..3).fold(false, |acc, x| acc || x > 2);
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// (0..3).any(|x| x > 2);
/// ```
#[clippy::version = "pre 1.29.0"]
@@ -1538,14 +1540,14 @@ declare_clippy_lint! {
/// operation is being performed.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let _ = (0..3).filter_map(|x| if x > 2 { Some(x) } else { None });
///
/// // As there is no transformation of the argument this could be written as:
/// let _ = (0..3).filter(|&x| x > 2);
/// ```
///
- /// ```rust
+ /// ```no_run
/// let _ = (0..4).filter_map(|x| Some(x + 1));
///
/// // As there is no conditional check on the argument this could be written as:
@@ -1568,14 +1570,14 @@ declare_clippy_lint! {
/// operation is being performed.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let _ = (0..3).find_map(|x| if x > 2 { Some(x) } else { None });
///
/// // As there is no transformation of the argument this could be written as:
/// let _ = (0..3).find(|&x| x > 2);
/// ```
///
- /// ```rust
+ /// ```no_run
/// let _ = (0..4).find_map(|x| Some(x + 1));
///
/// // As there is no conditional check on the argument this could be written as:
@@ -1598,13 +1600,13 @@ declare_clippy_lint! {
/// `iter_mut` directly.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # let vec = vec![3, 4, 5];
/// (&vec).into_iter();
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # let vec = vec![3, 4, 5];
/// (&vec).iter();
/// ```
@@ -1625,7 +1627,7 @@ declare_clippy_lint! {
/// completion, you can just use `for_each` instead.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let _ = (0..3).map(|x| x + 2).count();
/// ```
#[clippy::version = "1.39.0"]
@@ -1647,7 +1649,7 @@ declare_clippy_lint! {
/// data, but those are not yet rigorously defined.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// // Beware the UB
/// use std::mem::MaybeUninit;
///
@@ -1656,7 +1658,7 @@ declare_clippy_lint! {
///
/// Note that the following is OK:
///
- /// ```rust
+ /// ```no_run
/// use std::mem::MaybeUninit;
///
/// let _: [MaybeUninit<bool>; 5] = unsafe {
@@ -1677,7 +1679,7 @@ declare_clippy_lint! {
/// These can be written simply with `saturating_add/sub` methods.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # let y: u32 = 0;
/// # let x: u32 = 100;
/// let add = x.checked_add(y).unwrap_or(u32::MAX);
@@ -1686,7 +1688,7 @@ declare_clippy_lint! {
///
/// can be written using dedicated methods for saturating addition/subtraction as:
///
- /// ```rust
+ /// ```no_run
/// # let y: u32 = 0;
/// # let x: u32 = 100;
/// let add = x.saturating_add(y);
@@ -1707,7 +1709,7 @@ declare_clippy_lint! {
/// This is a no-op, and likely unintended
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// unsafe { (&() as *const ()).offset(1) };
/// ```
#[clippy::version = "1.41.0"]
@@ -1727,7 +1729,7 @@ declare_clippy_lint! {
/// symlink in windows. Using `!FileType::is_dir()` is a better way to that intention.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # || {
/// let metadata = std::fs::metadata("foo.txt")?;
/// let filetype = metadata.file_type();
@@ -1741,7 +1743,7 @@ declare_clippy_lint! {
///
/// should be written as:
///
- /// ```rust
+ /// ```no_run
/// # || {
/// let metadata = std::fs::metadata("foo.txt")?;
/// let filetype = metadata.file_type();
@@ -1767,13 +1769,13 @@ declare_clippy_lint! {
/// `_.as_deref()`.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # let opt = Some("".to_string());
/// opt.as_ref().map(String::as_str)
/// # ;
/// ```
/// Can be written as
- /// ```rust
+ /// ```no_run
/// # let opt = Some("".to_string());
/// opt.as_deref()
/// # ;
@@ -1792,14 +1794,14 @@ declare_clippy_lint! {
/// These can be shortened into `.get()`
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # let a = [1, 2, 3];
/// # let b = vec![1, 2, 3];
/// a[2..].iter().next();
/// b.iter().next();
/// ```
/// should be written as:
- /// ```rust
+ /// ```no_run
/// # let a = [1, 2, 3];
/// # let b = vec![1, 2, 3];
/// a.get(2);
@@ -1820,14 +1822,14 @@ declare_clippy_lint! {
/// It's less clear that we are pushing a single character.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # let mut string = String::new();
/// string.insert_str(0, "R");
/// string.push_str("R");
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # let mut string = String::new();
/// string.insert(0, 'R');
/// string.push('R');
@@ -1860,14 +1862,14 @@ declare_clippy_lint! {
/// side effects. Eagerly evaluating them can change the semantics of the program.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// // example code where clippy issues a warning
/// let opt: Option<u32> = None;
///
/// opt.unwrap_or_else(|| 42);
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// let opt: Option<u32> = None;
///
/// opt.unwrap_or(42);
@@ -1886,11 +1888,11 @@ declare_clippy_lint! {
/// Using `try_for_each` instead is more readable and idiomatic.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// (0..3).map(|t| Err(t)).collect::<Result<(), _>>();
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// (0..3).try_for_each(|t| Err(t));
/// ```
#[clippy::version = "1.49.0"]
@@ -1909,7 +1911,7 @@ declare_clippy_lint! {
/// [FromIterator documentation](https://doc.rust-lang.org/std/iter/trait.FromIterator.html)
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let five_fives = std::iter::repeat(5).take(5);
///
/// let v = Vec::from_iter(five_fives);
@@ -1917,7 +1919,7 @@ declare_clippy_lint! {
/// assert_eq!(v, vec![5, 5, 5, 5, 5]);
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// let five_fives = std::iter::repeat(5).take(5);
///
/// let v: Vec<i32> = five_fives.collect();
@@ -1939,7 +1941,7 @@ declare_clippy_lint! {
/// inside `inspect` at the beginning of the closure in `for_each`.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// [1,2,3,4,5].iter()
/// .inspect(|&x| println!("inspect the number: {}", x))
/// .for_each(|&x| {
@@ -1947,7 +1949,7 @@ declare_clippy_lint! {
/// });
/// ```
/// Can be written as
- /// ```rust
+ /// ```no_run
/// [1,2,3,4,5].iter()
/// .for_each(|&x| {
/// println!("inspect the number: {}", x);
@@ -1968,12 +1970,12 @@ declare_clippy_lint! {
/// Readability, this can be written more concisely by using `flatten`.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # let iter = vec![Some(1)].into_iter();
/// iter.filter_map(|x| x);
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # let iter = vec![Some(1)].into_iter();
/// iter.flatten();
/// ```
@@ -1991,12 +1993,12 @@ declare_clippy_lint! {
/// It can be written more concisely without the call to `map`.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let x = [1, 2, 3];
/// let y: Vec<_> = x.iter().map(|x| x).map(|x| 2*x).collect();
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// let x = [1, 2, 3];
/// let y: Vec<_> = x.iter().map(|x| 2*x).collect();
/// ```
@@ -2015,13 +2017,13 @@ declare_clippy_lint! {
/// readable.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # #[allow(unused)]
/// "Hello".bytes().nth(3);
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # #[allow(unused)]
/// "Hello".as_bytes().get(3);
/// ```
@@ -2040,13 +2042,13 @@ declare_clippy_lint! {
/// to why we are calling `to_vec` on something that is already a `Vec` or calling `to_owned` on something that is already owned.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let a = vec![1, 2, 3];
/// let b = a.to_vec();
/// let c = a.to_owned();
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// let a = vec![1, 2, 3];
/// let b = a.clone();
/// let c = a.clone();
@@ -2066,7 +2068,7 @@ declare_clippy_lint! {
/// readable.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # #![allow(unused)]
/// let some_vec = vec![0, 1, 2, 3];
///
@@ -2075,7 +2077,7 @@ declare_clippy_lint! {
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// let some_vec = vec![0, 1, 2, 3];
///
/// some_vec.len();
@@ -2104,7 +2106,7 @@ declare_clippy_lint! {
/// was the original intent, using `into_owned` instead.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # use std::borrow::Cow;
/// let s = "Hello world!";
/// let cow = Cow::Borrowed(s);
@@ -2113,7 +2115,7 @@ declare_clippy_lint! {
/// assert!(matches!(data, Cow::Borrowed(_)))
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # use std::borrow::Cow;
/// let s = "Hello world!";
/// let cow = Cow::Borrowed(s);
@@ -2122,7 +2124,7 @@ declare_clippy_lint! {
/// assert!(matches!(data, Cow::Borrowed(_)))
/// ```
/// or
- /// ```rust
+ /// ```no_run
/// # use std::borrow::Cow;
/// let s = "Hello world!";
/// let cow = Cow::Borrowed(s);
@@ -2146,7 +2148,7 @@ declare_clippy_lint! {
/// likely to be intended as a different number.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # let s = "";
/// for x in s.splitn(1, ":") {
/// // ..
@@ -2154,7 +2156,7 @@ declare_clippy_lint! {
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # let s = "";
/// for x in s.splitn(2, ":") {
/// // ..
@@ -2174,12 +2176,12 @@ declare_clippy_lint! {
/// These are both harder to read, as well as less performant.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let x: String = std::iter::repeat('x').take(10).collect();
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// let x: String = "x".repeat(10);
/// ```
#[clippy::version = "1.54.0"]
@@ -2231,13 +2233,13 @@ declare_clippy_lint! {
/// The function `split` is simpler and there is no performance difference in these cases, considering
/// that both functions return a lazy iterator.
/// ### Example
- /// ```rust
+ /// ```no_run
/// let str = "key=value=add";
/// let _ = str.splitn(3, '=').next().unwrap();
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// let str = "key=value=add";
/// let _ = str.split('=').next().unwrap();
/// ```
@@ -2261,13 +2263,13 @@ declare_clippy_lint! {
/// [#8148](https://github.com/rust-lang/rust-clippy/issues/8148).
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let path = std::path::Path::new("x");
/// foo(&path.to_string_lossy().to_string());
/// fn foo(s: &str) {}
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// let path = std::path::Path::new("x");
/// foo(&path.to_string_lossy());
/// fn foo(s: &str) {}
@@ -2286,13 +2288,13 @@ declare_clippy_lint! {
/// `.collect::<String>()` is more concise and might be more performant
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let vector = vec!["hello", "world"];
/// let output = vector.iter().map(|item| item.to_uppercase()).collect::<Vec<String>>().join("");
/// println!("{}", output);
/// ```
/// The correct use would be:
- /// ```rust
+ /// ```no_run
/// let vector = vec!["hello", "world"];
/// let output = vector.iter().map(|item| item.to_uppercase()).collect::<String>();
/// println!("{}", output);
@@ -2319,13 +2321,13 @@ declare_clippy_lint! {
/// Redundant code and improving readability.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let a = Some(&1);
/// let b = a.as_deref(); // goes from Option<&i32> to Option<&i32>
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// let a = Some(&1);
/// let b = a;
/// ```
@@ -2345,13 +2347,13 @@ declare_clippy_lint! {
/// `is_digit(..)` is slower and requires specifying the radix.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let c: char = '6';
/// c.is_digit(10);
/// c.is_digit(16);
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// let c: char = '6';
/// c.is_ascii_digit();
/// c.is_ascii_hexdigit();
@@ -2371,12 +2373,12 @@ declare_clippy_lint! {
/// In this case the modification is useless as it's a temporary that cannot be read from afterwards.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let x = Some(3);
/// x.as_ref().take();
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// let x = Some(3);
/// x.as_ref();
/// ```
@@ -2394,7 +2396,7 @@ declare_clippy_lint! {
/// It's either a mistake or confusing.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// "1234".replace("12", "12");
/// "1234".replacen("12", "12", 1);
/// ```
@@ -2417,12 +2419,12 @@ declare_clippy_lint! {
/// to account for similar patterns.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let x = true;
/// x.then_some("a").unwrap_or("b");
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// let x = true;
/// if x { "a" } else { "b" };
/// ```
@@ -2444,12 +2446,12 @@ declare_clippy_lint! {
///
/// ### Example
///
- /// ```rust
+ /// ```no_run
/// let a = [123].iter();
/// let b = Some(123).into_iter();
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// use std::iter;
/// let a = iter::once(&123);
/// let b = iter::once(123);
@@ -2475,13 +2477,13 @@ declare_clippy_lint! {
///
/// ### Example
///
- /// ```rust
+ /// ```no_run
/// use std::{slice, option};
/// let a: slice::Iter<i32> = [].iter();
/// let f: option::IntoIter<i32> = None.into_iter();
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// use std::iter;
/// let a: iter::Empty<i32> = iter::empty();
/// let b: iter::Empty<i32> = iter::empty();
@@ -2511,7 +2513,7 @@ declare_clippy_lint! {
/// faster in those cases.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # let vec = vec![1_u8];
/// let count = vec.iter().filter(|x| **x == 0u8).count();
/// ```
@@ -2537,12 +2539,12 @@ declare_clippy_lint! {
/// `str::len()`.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// "hello".bytes().count();
/// String::from("hello").bytes().count();
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// "hello".len();
/// String::from("hello").len();
/// ```
@@ -2561,13 +2563,13 @@ declare_clippy_lint! {
/// `ends_with` is case-sensitive and may not detect files with a valid extension.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// fn is_rust_file(filename: &str) -> bool {
/// filename.ends_with(".rs")
/// }
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// fn is_rust_file(filename: &str) -> bool {
/// let filename = std::path::Path::new(filename);
/// filename.extension()
@@ -2590,13 +2592,13 @@ declare_clippy_lint! {
/// result.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let x = vec![2, 3, 5];
/// let first_element = x.get(0);
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// let x = vec![2, 3, 5];
/// let first_element = x.first();
/// ```
@@ -2616,13 +2618,13 @@ declare_clippy_lint! {
/// Concise code helps focusing on behavior instead of boilerplate.
///
/// ### Examples
- /// ```rust
+ /// ```no_run
/// let foo: Option<i32> = None;
/// foo.map_or(Err("error"), |v| Ok(v));
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// let foo: Option<i32> = None;
/// foo.ok_or("error");
/// ```
@@ -2642,7 +2644,7 @@ declare_clippy_lint! {
/// Readability, this can be written more concisely
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let x = vec![42, 43];
/// let y = x.iter();
/// let z = y.map(|i| *i);
@@ -2650,7 +2652,7 @@ declare_clippy_lint! {
///
/// The correct use would be:
///
- /// ```rust
+ /// ```no_run
/// let x = vec![42, 43];
/// let y = x.iter();
/// let z = y.cloned();
@@ -2670,7 +2672,7 @@ declare_clippy_lint! {
///
/// ### Example
/// Before:
- /// ```rust
+ /// ```no_run
/// use std::fmt;
///
/// #[derive(Debug)]
@@ -2772,7 +2774,7 @@ declare_clippy_lint! {
/// guarantee.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// use std::sync::{Arc, Mutex};
///
/// let mut value_rc = Arc::new(Mutex::new(42_u8));
@@ -2782,7 +2784,7 @@ declare_clippy_lint! {
/// *value += 1;
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// use std::sync::{Arc, Mutex};
///
/// let mut value_rc = Arc::new(Mutex::new(42_u8));
@@ -2807,7 +2809,7 @@ declare_clippy_lint! {
/// necessary. I don't know the worst case.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// use std::fs::OpenOptions;
///
/// OpenOptions::new().read(true).truncate(true);
@@ -2828,7 +2830,7 @@ declare_clippy_lint! {
/// previous defined path.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// use std::path::PathBuf;
///
/// let mut x = PathBuf::from("/foo");
@@ -2837,7 +2839,7 @@ declare_clippy_lint! {
/// ```
/// Could be written:
///
- /// ```rust
+ /// ```no_run
/// use std::path::PathBuf;
///
/// let mut x = PathBuf::from("/foo");
@@ -2859,13 +2861,13 @@ declare_clippy_lint! {
/// The code is better expressed with `.enumerate()`.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # let x = vec![1];
/// let _ = x.iter().zip(0..x.len());
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # let x = vec![1];
/// let _ = x.iter().enumerate();
/// ```
@@ -2890,13 +2892,13 @@ declare_clippy_lint! {
/// the string is the intention behind this, `clone()` should be used.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// fn main() {
/// let x = String::from("hello world").repeat(1);
/// }
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// fn main() {
/// let x = String::from("hello world").clone();
/// }
@@ -2930,12 +2932,12 @@ declare_clippy_lint! {
/// issue linked above.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let mut vec = vec![2, 1, 3];
/// vec.sort();
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// let mut vec = vec![2, 1, 3];
/// vec.sort_unstable();
/// ```
@@ -2963,14 +2965,14 @@ declare_clippy_lint! {
/// assert_eq!(any_box.type_id(), TypeId::of::<i32>()); // ⚠️ this fails!
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// use std::any::{Any, TypeId};
///
/// let any_box: Box<dyn Any> = Box::new(42_i32);
/// assert_eq!((*any_box).type_id(), TypeId::of::<i32>());
/// // ^ dereference first, to call `type_id` on `dyn Any`
/// ```
- #[clippy::version = "1.72.0"]
+ #[clippy::version = "1.73.0"]
pub TYPE_ID_ON_BOX,
suspicious,
"calling `.type_id()` on `Box<dyn Any>`"
@@ -2984,7 +2986,7 @@ declare_clippy_lint! {
/// Hashing a unit value doesn't do anything as the implementation of `Hash` for `()` is a no-op.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # use std::hash::Hash;
/// # use std::collections::hash_map::DefaultHasher;
/// # enum Foo { Empty, WithValue(u8) }
@@ -2997,7 +2999,7 @@ declare_clippy_lint! {
/// }
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # use std::hash::Hash;
/// # use std::collections::hash_map::DefaultHasher;
/// # enum Foo { Empty, WithValue(u8) }
@@ -3030,14 +3032,14 @@ declare_clippy_lint! {
/// imported by a use statement, then it will need to be added manually.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # struct A;
/// # impl A { fn foo(&self) {} }
/// # let mut vec: Vec<A> = Vec::new();
/// vec.sort_by(|a, b| a.foo().cmp(&b.foo()));
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # struct A;
/// # impl A { fn foo(&self) {} }
/// # let mut vec: Vec<A> = Vec::new();
@@ -3057,12 +3059,12 @@ declare_clippy_lint! {
/// This is probably an argument inversion mistake.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// vec![1, 2, 3, 4, 5].resize(0, 5)
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// vec![1, 2, 3, 4, 5].clear()
/// ```
#[clippy::version = "1.46.0"]
@@ -3111,14 +3113,14 @@ declare_clippy_lint! {
///
/// ### Example
///
- /// ```
+ /// ```no_run
/// # use std::collections::HashMap;
/// let map: HashMap<u32, u32> = HashMap::new();
/// let values = map.iter().map(|(_, value)| value).collect::<Vec<_>>();
/// ```
///
/// Use instead:
- /// ```
+ /// ```no_run
/// # use std::collections::HashMap;
/// let map: HashMap<u32, u32> = HashMap::new();
/// let values = map.values().collect::<Vec<_>>();
@@ -3184,14 +3186,14 @@ declare_clippy_lint! {
/// this exact scenario.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # use std::io;
/// fn foo<T: io::Seek>(t: &mut T) {
/// t.seek(io::SeekFrom::Start(0));
/// }
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # use std::io;
/// fn foo<T: io::Seek>(t: &mut T) {
/// t.rewind();
@@ -3213,12 +3215,12 @@ declare_clippy_lint! {
/// when this allocation may not be needed.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # let iterator = vec![1].into_iter();
/// let len = iterator.collect::<Vec<_>>().len();
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # let iterator = vec![1].into_iter();
/// let len = iterator.count();
/// ```
@@ -3241,11 +3243,11 @@ declare_clippy_lint! {
/// which is likely not what was intended.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// std::process::Command::new("echo").arg("-n hello").spawn().unwrap();
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// std::process::Command::new("echo").args(["-n", "hello"]).spawn().unwrap();
/// ```
#[clippy::version = "1.69.0"]
@@ -3264,12 +3266,12 @@ declare_clippy_lint! {
/// Calling `.clear()` also makes the intent clearer.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let mut v = vec![1, 2, 3];
/// v.drain(..);
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// let mut v = vec![1, 2, 3];
/// v.clear();
/// ```
@@ -3287,12 +3289,12 @@ declare_clippy_lint! {
/// `.next_back()` is cleaner.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # let foo = [0; 10];
/// foo.iter().rev().next();
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # let foo = [0; 10];
/// foo.iter().next_back();
/// ```
@@ -3320,13 +3322,13 @@ declare_clippy_lint! {
/// to keep the capacity on the original `Vec`.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// fn remove_all(v: &mut Vec<i32>) -> Vec<i32> {
/// v.drain(..).collect()
/// }
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// use std::mem;
/// fn remove_all(v: &mut Vec<i32>) -> Vec<i32> {
/// mem::take(v)
@@ -3354,11 +3356,11 @@ declare_clippy_lint! {
/// desirable in those cases.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// vec![1, 2, 3].iter().fold(Some(0i32), |sum, i| sum?.checked_add(*i));
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// vec![1, 2, 3].iter().try_fold(0i32, |sum, i| sum.checked_add(*i));
/// ```
#[clippy::version = "1.72.0"]
@@ -3368,6 +3370,7 @@ declare_clippy_lint! {
}
declare_clippy_lint! {
+ /// ### What it does
/// Looks for calls to [`Stdin::read_line`] to read a line from the standard input
/// into a string, then later attempting to parse this string into a type without first trimming it, which will
/// always fail because the string has a trailing newline in it.
@@ -3390,7 +3393,7 @@ declare_clippy_lint! {
/// // ^^^^^^^^^^^ remove the trailing newline
/// assert_eq!(num, 42);
/// ```
- #[clippy::version = "1.72.0"]
+ #[clippy::version = "1.73.0"]
pub READ_LINE_WITHOUT_TRIM,
correctness,
"calling `Stdin::read_line`, then trying to parse it without first trimming"
@@ -3409,16 +3412,16 @@ declare_clippy_lint! {
/// for situations where that additional performance is absolutely necessary.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # let c = 'c';
/// "\\.+*?()|[]{}^$#&-~".chars().any(|x| x == c);
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # let c = 'c';
/// matches!(c, '\\' | '.' | '+' | '*' | '(' | ')' | '|' | '[' | ']' | '{' | '}' | '^' | '$' | '#' | '&' | '-' | '~');
/// ```
- #[clippy::version = "1.72.0"]
+ #[clippy::version = "1.73.0"]
pub STRING_LIT_CHARS_ANY,
restriction,
"checks for `<string_lit>.chars().any(|i| i == c)`"
@@ -3438,13 +3441,13 @@ declare_clippy_lint! {
/// so it can be safely ignored or unwrapped.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// fn hex_encode(bytes: &[u8]) -> String {
/// bytes.iter().map(|b| format!("{b:02X}")).collect()
/// }
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// use std::fmt::Write;
/// fn hex_encode(bytes: &[u8]) -> String {
/// bytes.iter().fold(String::new(), |mut output, b| {
@@ -3453,7 +3456,7 @@ declare_clippy_lint! {
/// })
/// }
/// ```
- #[clippy::version = "1.72.0"]
+ #[clippy::version = "1.73.0"]
pub FORMAT_COLLECT,
perf,
"`format!`ing every element in a collection, then collecting the strings into a new `String`"
@@ -3468,13 +3471,13 @@ declare_clippy_lint! {
/// nothing. If not, the call should be removed.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let v = vec![1, 2, 3];
/// let x = v.iter().skip(0).collect::<Vec<_>>();
/// let y = v.iter().collect::<Vec<_>>();
/// assert_eq!(x, y);
/// ```
- #[clippy::version = "1.72.0"]
+ #[clippy::version = "1.73.0"]
pub ITER_SKIP_ZERO,
correctness,
"disallows `.skip(0)`"
@@ -3494,18 +3497,18 @@ declare_clippy_lint! {
/// This can create differing behavior, so better safe than sorry.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # fn really_expensive_fn(i: i32) -> i32 { i }
/// # let v = vec![];
/// _ = v.into_iter().filter_map(|i| (i % 2 == 0).then(|| really_expensive_fn(i)));
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # fn really_expensive_fn(i: i32) -> i32 { i }
/// # let v = vec![];
/// _ = v.into_iter().filter(|i| i % 2 == 0).map(|i| really_expensive_fn(i));
/// ```
- #[clippy::version = "1.72.0"]
+ #[clippy::version = "1.73.0"]
pub FILTER_MAP_BOOL_THEN,
style,
"checks for usage of `bool::then` in `Iterator::filter_map`"
@@ -3520,7 +3523,7 @@ declare_clippy_lint! {
/// can access the lock while this writer is active.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// use std::sync::RwLock;
/// fn assert_is_zero(lock: &RwLock<i32>) {
/// let num = lock.write().unwrap();
@@ -3529,7 +3532,7 @@ declare_clippy_lint! {
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// use std::sync::RwLock;
/// fn assert_is_zero(lock: &RwLock<i32>) {
/// let num = lock.read().unwrap();
@@ -3553,11 +3556,11 @@ declare_clippy_lint! {
/// This is most likely not what the user intended to do.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// for _ in [1, 2, 3].iter().take(4) {}
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// for _ in [1, 2, 3].iter() {}
/// ```
#[clippy::version = "1.74.0"]
@@ -3586,14 +3589,14 @@ declare_clippy_lint! {
/// therefore ignored.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # use std::path::Path;
/// fn is_markdown(path: &Path) -> bool {
/// path.ends_with(".md")
/// }
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # use std::path::Path;
/// fn is_markdown(path: &Path) -> bool {
/// path.extension().is_some_and(|ext| ext == "md")
@@ -3613,14 +3616,14 @@ declare_clippy_lint! {
/// The `as_str()` conversion is pointless and can be removed for simplicity and cleanliness.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # #![allow(unused)]
/// let owned_string = "This is a string".to_owned();
/// owned_string.as_str().as_bytes();
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # #![allow(unused)]
/// let owned_string = "This is a string".to_owned();
/// owned_string.as_bytes();
@@ -3631,6 +3634,55 @@ declare_clippy_lint! {
"`as_str` used to call a method on `str` that is also available on `String`"
}
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for usage of `waker.clone().wake()`
+ ///
+ /// ### Why is this bad?
+ /// Cloning the waker is not necessary, `wake_by_ref()` enables the same operation
+ /// without extra cloning/dropping.
+ ///
+ /// ### Example
+ /// ```rust,ignore
+ /// waker.clone().wake();
+ /// ```
+ /// Should be written
+ /// ```rust,ignore
+ /// waker.wake_by_ref();
+ /// ```
+ #[clippy::version = "1.75.0"]
+ pub WAKER_CLONE_WAKE,
+ perf,
+ "cloning a `Waker` only to wake it"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for calls to `TryInto::try_into` and `TryFrom::try_from` when their infallible counterparts
+ /// could be used.
+ ///
+ /// ### Why is this bad?
+ /// In those cases, the `TryInto` and `TryFrom` trait implementation is a blanket impl that forwards
+ /// to `Into` or `From`, which always succeeds.
+ /// The returned `Result<_, Infallible>` requires error handling to get the contained value
+ /// even though the conversion can never fail.
+ ///
+ /// ### Example
+ /// ```rust
+ /// let _: Result<i64, _> = 1i32.try_into();
+ /// let _: Result<i64, _> = <_>::try_from(1i32);
+ /// ```
+ /// Use `from`/`into` instead:
+ /// ```rust
+ /// let _: i64 = 1i32.into();
+ /// let _: i64 = <_>::from(1i32);
+ /// ```
+ #[clippy::version = "1.75.0"]
+ pub UNNECESSARY_FALLIBLE_CONVERSIONS,
+ style,
+ "calling the `try_from` and `try_into` trait methods when `From`/`Into` is implemented"
+}
+
pub struct Methods {
avoid_breaking_exported_api: bool,
msrv: Msrv,
@@ -3776,6 +3828,8 @@ impl_lint_pass!(Methods => [
ITER_OUT_OF_BOUNDS,
PATH_ENDS_WITH_EXT,
REDUNDANT_AS_STR,
+ WAKER_CLONE_WAKE,
+ UNNECESSARY_FALLIBLE_CONVERSIONS,
]);
/// Extracts a method call name, args, and `Span` of the method name.
@@ -3802,6 +3856,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
match expr.kind {
hir::ExprKind::Call(func, args) => {
from_iter_instead_of_collect::check(cx, expr, args, func);
+ unnecessary_fallible_conversions::check_function(cx, expr, func);
},
hir::ExprKind::MethodCall(method_call, receiver, args, _) => {
let method_span = method_call.ident.span;
@@ -3877,21 +3932,21 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
}
if sig.decl.implicit_self.has_implicit_self()
- && !(self.avoid_breaking_exported_api
+ && !(self.avoid_breaking_exported_api
&& cx.effective_visibilities.is_exported(impl_item.owner_id.def_id))
- && let Some(first_arg) = iter_input_pats(sig.decl, cx.tcx.hir().body(id)).next()
- && let Some(first_arg_ty) = first_arg_ty_opt
- {
- wrong_self_convention::check(
- cx,
- name,
- self_ty,
- first_arg_ty,
- first_arg.pat.span,
- implements_trait,
- false
- );
- }
+ && let Some(first_arg) = iter_input_pats(sig.decl, cx.tcx.hir().body(id)).next()
+ && let Some(first_arg_ty) = first_arg_ty_opt
+ {
+ wrong_self_convention::check(
+ cx,
+ name,
+ self_ty,
+ first_arg_ty,
+ first_arg.pat.span,
+ implements_trait,
+ false,
+ );
+ }
}
// if this impl block implements a trait, lint in trait definition instead
@@ -3976,10 +4031,16 @@ impl Methods {
},
("all", [arg]) => {
if let Some(("cloned", recv2, [], _, _)) = method_call(recv) {
- iter_overeager_cloned::check(cx, expr, recv, recv2,
- iter_overeager_cloned::Op::NeedlessMove(name, arg), false);
+ iter_overeager_cloned::check(
+ cx,
+ expr,
+ recv,
+ recv2,
+ iter_overeager_cloned::Op::NeedlessMove(name, arg),
+ false,
+ );
}
- }
+ },
("and_then", [arg]) => {
let biom_option_linted = bind_instead_of_map::OptionAndThenSome::check(cx, expr, recv, arg);
let biom_result_linted = bind_instead_of_map::ResultAndThenOk::check(cx, expr, recv, arg);
@@ -3987,24 +4048,35 @@ impl Methods {
unnecessary_lazy_eval::check(cx, expr, recv, arg, "and");
}
},
- ("any", [arg]) => {
- match method_call(recv) {
- Some(("cloned", recv2, [], _, _)) => iter_overeager_cloned::check(cx, expr, recv, recv2, iter_overeager_cloned::Op::NeedlessMove(name, arg), false),
- Some(("chars", recv, _, _, _)) if let ExprKind::Closure(arg) = arg.kind
- && let body = cx.tcx.hir().body(arg.body)
- && let [param] = body.params => {
- string_lit_chars_any::check(cx, expr, recv, param, peel_blocks(body.value), &self.msrv);
- }
- _ => {}
- }
- }
+ ("any", [arg]) => match method_call(recv) {
+ Some(("cloned", recv2, [], _, _)) => iter_overeager_cloned::check(
+ cx,
+ expr,
+ recv,
+ recv2,
+ iter_overeager_cloned::Op::NeedlessMove(name, arg),
+ false,
+ ),
+ Some(("chars", recv, _, _, _))
+ if let ExprKind::Closure(arg) = arg.kind
+ && let body = cx.tcx.hir().body(arg.body)
+ && let [param] = body.params =>
+ {
+ string_lit_chars_any::check(cx, expr, recv, param, peel_blocks(body.value), &self.msrv);
+ },
+ _ => {},
+ },
("arg", [arg]) => {
suspicious_command_arg_space::check(cx, recv, arg, span);
- }
+ },
("as_deref" | "as_deref_mut", []) => {
needless_option_as_deref::check(cx, expr, recv, name);
},
- ("as_bytes" | "is_empty", []) => if let Some(("as_str", recv, [], as_str_span, _)) = method_call(recv) { redundant_as_str::check(cx, expr, recv, as_str_span, span); },
+ ("as_bytes" | "is_empty", []) => {
+ if let Some(("as_str", recv, [], as_str_span, _)) = method_call(recv) {
+ redundant_as_str::check(cx, expr, recv, as_str_span, span);
+ }
+ },
("as_mut", []) => useless_asref::check(cx, expr, "as_mut", recv),
("as_ref", []) => useless_asref::check(cx, expr, "as_ref", recv),
("assume_init", []) => uninit_assumed_init::check(cx, expr, recv),
@@ -4026,12 +4098,14 @@ impl Methods {
},
Some(("drain", recv, args, ..)) => {
drain_collect::check(cx, args, expr, recv);
- }
+ },
_ => {},
}
},
("count", []) if is_trait_method(cx, expr, sym::Iterator) => match method_call(recv) {
- Some(("cloned", recv2, [], _, _)) => iter_overeager_cloned::check(cx, expr, recv, recv2, iter_overeager_cloned::Op::RmCloned , false),
+ Some(("cloned", recv2, [], _, _)) => {
+ iter_overeager_cloned::check(cx, expr, recv, recv2, iter_overeager_cloned::Op::RmCloned, false);
+ },
Some((name2 @ ("into_iter" | "iter" | "iter_mut"), recv2, [], _, _)) => {
iter_count::check(cx, expr, recv2, name2);
},
@@ -4059,7 +4133,9 @@ impl Methods {
("expect", [_]) => {
match method_call(recv) {
Some(("ok", recv, [], _, _)) => ok_expect::check(cx, expr, recv),
- Some(("err", recv, [], err_span, _)) => err_expect::check(cx, expr, recv, span, err_span, &self.msrv),
+ Some(("err", recv, [], err_span, _)) => {
+ err_expect::check(cx, expr, recv, span, err_span, &self.msrv);
+ },
_ => unwrap_expect_used::check(
cx,
expr,
@@ -4086,13 +4162,19 @@ impl Methods {
string_extend_chars::check(cx, expr, recv, arg);
extend_with_drain::check(cx, expr, recv, arg);
},
- (name @ ( "filter" | "find" ) , [arg]) => {
+ (name @ ("filter" | "find"), [arg]) => {
if let Some(("cloned", recv2, [], _span2, _)) = method_call(recv) {
// if `arg` has side-effect, the semantic will change
- iter_overeager_cloned::check(cx, expr, recv, recv2,
- iter_overeager_cloned::Op::FixClosure(name, arg), false);
+ iter_overeager_cloned::check(
+ cx,
+ expr,
+ recv,
+ recv2,
+ iter_overeager_cloned::Op::FixClosure(name, arg),
+ false,
+ );
}
- }
+ },
("filter_map", [arg]) => {
unnecessary_filter_map::check(cx, expr, arg, name);
filter_map_bool_then::check(cx, expr, arg, call_span);
@@ -4106,20 +4188,34 @@ impl Methods {
flat_map_option::check(cx, expr, arg, span);
},
("flatten", []) => match method_call(recv) {
- Some(("map", recv, [map_arg], map_span, _)) => map_flatten::check(cx, expr, recv, map_arg, map_span),
- Some(("cloned", recv2, [], _, _)) => iter_overeager_cloned::check(cx, expr, recv, recv2, iter_overeager_cloned::Op::LaterCloned , true),
+ Some(("map", recv, [map_arg], map_span, _)) => {
+ map_flatten::check(cx, expr, recv, map_arg, map_span);
+ },
+ Some(("cloned", recv2, [], _, _)) => iter_overeager_cloned::check(
+ cx,
+ expr,
+ recv,
+ recv2,
+ iter_overeager_cloned::Op::LaterCloned,
+ true,
+ ),
_ => {},
},
("fold", [init, acc]) => {
manual_try_fold::check(cx, expr, init, acc, call_span, &self.msrv);
unnecessary_fold::check(cx, expr, init, acc, span);
},
- ("for_each", [arg]) => {
- match method_call(recv) {
- Some(("inspect", _, [_], span2, _)) => inspect_for_each::check(cx, expr, span2),
- Some(("cloned", recv2, [], _, _)) => iter_overeager_cloned::check(cx, expr, recv, recv2, iter_overeager_cloned::Op::NeedlessMove(name, arg), false),
- _ => {}
- }
+ ("for_each", [arg]) => match method_call(recv) {
+ Some(("inspect", _, [_], span2, _)) => inspect_for_each::check(cx, expr, span2),
+ Some(("cloned", recv2, [], _, _)) => iter_overeager_cloned::check(
+ cx,
+ expr,
+ recv,
+ recv2,
+ iter_overeager_cloned::Op::NeedlessMove(name, arg),
+ false,
+ ),
+ _ => {},
},
("get", [arg]) => {
get_first::check(cx, expr, recv, arg);
@@ -4143,8 +4239,14 @@ impl Methods {
},
("last", []) => {
if let Some(("cloned", recv2, [], _span2, _)) = method_call(recv) {
- iter_overeager_cloned::check(cx, expr, recv, recv2,
- iter_overeager_cloned::Op::LaterCloned , false);
+ iter_overeager_cloned::check(
+ cx,
+ expr,
+ recv,
+ recv2,
+ iter_overeager_cloned::Op::LaterCloned,
+ false,
+ );
}
},
("lock", []) => {
@@ -4154,14 +4256,23 @@ impl Methods {
if name == "map" {
map_clone::check(cx, expr, recv, m_arg, &self.msrv);
match method_call(recv) {
- Some((map_name @ ("iter" | "into_iter"), recv2, _, _, _)) => iter_kv_map::check(cx, map_name, expr, recv2, m_arg),
- Some(("cloned", recv2, [], _, _)) => iter_overeager_cloned::check(cx, expr, recv, recv2, iter_overeager_cloned::Op::NeedlessMove(name, m_arg), false),
- _ => {}
+ Some((map_name @ ("iter" | "into_iter"), recv2, _, _, _)) => {
+ iter_kv_map::check(cx, map_name, expr, recv2, m_arg);
+ },
+ Some(("cloned", recv2, [], _, _)) => iter_overeager_cloned::check(
+ cx,
+ expr,
+ recv,
+ recv2,
+ iter_overeager_cloned::Op::NeedlessMove(name, m_arg),
+ false,
+ ),
+ _ => {},
}
} else {
map_err_ignore::check(cx, expr, m_arg);
}
- if let Some((name, recv2, args, span2,_)) = method_call(recv) {
+ if let Some((name, recv2, args, span2, _)) = method_call(recv) {
match (name, args) {
("as_mut", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, true, &self.msrv),
("as_ref", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, false, &self.msrv),
@@ -4183,20 +4294,34 @@ impl Methods {
("next", []) => {
if let Some((name2, recv2, args2, _, _)) = method_call(recv) {
match (name2, args2) {
- ("cloned", []) => iter_overeager_cloned::check(cx, expr, recv, recv2, iter_overeager_cloned::Op::LaterCloned, false),
+ ("cloned", []) => iter_overeager_cloned::check(
+ cx,
+ expr,
+ recv,
+ recv2,
+ iter_overeager_cloned::Op::LaterCloned,
+ false,
+ ),
("filter", [arg]) => filter_next::check(cx, expr, recv2, arg),
("filter_map", [arg]) => filter_map_next::check(cx, expr, recv2, arg, &self.msrv),
("iter", []) => iter_next_slice::check(cx, expr, recv2),
("skip", [arg]) => iter_skip_next::check(cx, expr, recv2, arg),
("skip_while", [_]) => skip_while_next::check(cx, expr),
- ("rev", [])=> manual_next_back::check(cx, expr, recv, recv2),
+ ("rev", []) => manual_next_back::check(cx, expr, recv, recv2),
_ => {},
}
}
},
("nth", [n_arg]) => match method_call(recv) {
Some(("bytes", recv2, [], _, _)) => bytes_nth::check(cx, expr, recv2, n_arg),
- Some(("cloned", recv2, [], _, _)) => iter_overeager_cloned::check(cx, expr, recv, recv2, iter_overeager_cloned::Op::LaterCloned , false),
+ Some(("cloned", recv2, [], _, _)) => iter_overeager_cloned::check(
+ cx,
+ expr,
+ recv,
+ recv2,
+ iter_overeager_cloned::Op::LaterCloned,
+ false,
+ ),
Some(("iter", recv2, [], _, _)) => iter_nth::check(cx, expr, recv2, recv, n_arg, false),
Some(("iter_mut", recv2, [], _, _)) => iter_nth::check(cx, expr, recv2, recv, n_arg, true),
_ => iter_nth_zero::check(cx, expr, recv, n_arg),
@@ -4221,7 +4346,7 @@ impl Methods {
},
("read_line", [arg]) => {
read_line_without_trim::check(cx, expr, recv, arg);
- }
+ },
("repeat", [arg]) => {
repeat_once::check(cx, expr, recv, arg);
},
@@ -4252,10 +4377,16 @@ impl Methods {
iter_out_of_bounds::check_skip(cx, expr, recv, arg);
if let Some(("cloned", recv2, [], _span2, _)) = method_call(recv) {
- iter_overeager_cloned::check(cx, expr, recv, recv2,
- iter_overeager_cloned::Op::LaterCloned , false);
+ iter_overeager_cloned::check(
+ cx,
+ expr,
+ recv,
+ recv2,
+ iter_overeager_cloned::Op::LaterCloned,
+ false,
+ );
}
- }
+ },
("sort", []) => {
stable_sort_primitive::check(cx, expr, recv);
},
@@ -4280,8 +4411,14 @@ impl Methods {
("take", [arg]) => {
iter_out_of_bounds::check_take(cx, expr, recv, arg);
if let Some(("cloned", recv2, [], _span2, _)) = method_call(recv) {
- iter_overeager_cloned::check(cx, expr, recv, recv2,
- iter_overeager_cloned::Op::LaterCloned, false);
+ iter_overeager_cloned::check(
+ cx,
+ expr,
+ recv,
+ recv2,
+ iter_overeager_cloned::Op::LaterCloned,
+ false,
+ );
}
},
("take", []) => needless_option_take::check(cx, expr, recv),
@@ -4291,6 +4428,9 @@ impl Methods {
}
unnecessary_lazy_eval::check(cx, expr, recv, arg, "then_some");
},
+ ("try_into", []) if is_trait_method(cx, expr, sym::TryInto) => {
+ unnecessary_fallible_conversions::check_method(cx, expr);
+ },
("to_owned", []) => {
if !suspicious_to_owned::check(cx, expr, recv) {
implicit_clone::check(cx, name, expr, recv);
@@ -4301,7 +4441,7 @@ impl Methods {
},
("type_id", []) => {
type_id_on_box::check(cx, recv, expr.span);
- }
+ },
("unwrap", []) => {
match method_call(recv) {
Some(("get", recv, [get_arg], _, _)) => {
@@ -4353,7 +4493,7 @@ impl Methods {
},
("unwrap_or_default" | "unwrap_unchecked" | "unwrap_err_unchecked", []) => {
unnecessary_literal_unwrap::check(cx, expr, recv, name, args);
- }
+ },
("unwrap_or_else", [u_arg]) => {
match method_call(recv) {
Some(("map", recv, [map_arg], _, _))
@@ -4364,9 +4504,12 @@ impl Methods {
}
unnecessary_literal_unwrap::check(cx, expr, recv, name, args);
},
+ ("wake", []) => {
+ waker_clone_wake::check(cx, expr, recv);
+ },
("write", []) => {
readonly_write_lock::check(cx, expr, recv);
- }
+ },
("zip", [arg]) => {
if let ExprKind::MethodCall(name, iter_recv, [], _) = recv.kind
&& name.ident.name == sym::iter
diff --git a/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs b/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs
index dbd965d65..2ef71be32 100644
--- a/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs
@@ -17,7 +17,7 @@ use rustc_lint::LateContext;
use rustc_middle::hir::nested_filter;
use rustc_middle::ty::{self, AssocKind, ClauseKind, EarlyBinder, GenericArg, GenericArgKind, Ty};
use rustc_span::symbol::Ident;
-use rustc_span::{sym, Span, Symbol};
+use rustc_span::{sym, Span};
const NEEDLESS_COLLECT_MSG: &str = "avoid using `collect()` when not needed";
@@ -87,21 +87,17 @@ pub(super) fn check<'tcx>(
}
},
Node::Local(l) => {
- if let PatKind::Binding(BindingAnnotation::NONE | BindingAnnotation::MUT, id, _, None)
- = l.pat.kind
+ if let PatKind::Binding(BindingAnnotation::NONE | BindingAnnotation::MUT, id, _, None) = l.pat.kind
&& let ty = cx.typeck_results().expr_ty(collect_expr)
- && [sym::Vec, sym::VecDeque, sym::BinaryHeap, sym::LinkedList].into_iter()
+ && [sym::Vec, sym::VecDeque, sym::BinaryHeap, sym::LinkedList]
+ .into_iter()
.any(|item| is_type_diagnostic_item(cx, ty, item))
&& let iter_ty = cx.typeck_results().expr_ty(iter_expr)
&& let Some(block) = get_enclosing_block(cx, l.hir_id)
&& let Some(iter_calls) = detect_iter_and_into_iters(block, id, cx, get_captured_ids(cx, iter_ty))
&& let [iter_call] = &*iter_calls
{
- let mut used_count_visitor = UsedCountVisitor {
- cx,
- id,
- count: 0,
- };
+ let mut used_count_visitor = UsedCountVisitor { cx, id, count: 0 };
walk_block(&mut used_count_visitor, block);
if used_count_visitor.count > 1 {
return;
@@ -117,13 +113,11 @@ pub(super) fn check<'tcx>(
span,
NEEDLESS_COLLECT_MSG,
|diag| {
- let iter_replacement = format!("{}{}", Sugg::hir(cx, iter_expr, ".."), iter_call.get_iter_method(cx));
+ let iter_replacement =
+ format!("{}{}", Sugg::hir(cx, iter_expr, ".."), iter_call.get_iter_method(cx));
diag.multipart_suggestion(
iter_call.get_suggestion_text(),
- vec![
- (l.span, String::new()),
- (iter_call.span, iter_replacement)
- ],
+ vec![(l.span, String::new()), (iter_call.span, iter_replacement)],
Applicability::MaybeIncorrect,
);
},
@@ -175,11 +169,12 @@ fn check_collect_into_intoiterator<'tcx>(
.into_iter()
.filter_map(|p| {
if let ClauseKind::Trait(t) = p.kind().skip_binder()
- && cx.tcx.is_diagnostic_item(sym::IntoIterator,t.trait_ref.def_id) {
- Some(t.self_ty())
- } else {
- None
- }
+ && cx.tcx.is_diagnostic_item(sym::IntoIterator, t.trait_ref.def_id)
+ {
+ Some(t.self_ty())
+ } else {
+ None
+ }
})
.any(|ty| ty == inputs[arg_idx])
{
@@ -207,14 +202,13 @@ fn is_is_empty_sig(cx: &LateContext<'_>, call_id: HirId) -> bool {
/// Checks if `<iter_ty as Iterator>::Item` is the same as `<collect_ty as IntoIter>::Item`
fn iterates_same_ty<'tcx>(cx: &LateContext<'tcx>, iter_ty: Ty<'tcx>, collect_ty: Ty<'tcx>) -> bool {
- let item = Symbol::intern("Item");
if let Some(iter_trait) = cx.tcx.get_diagnostic_item(sym::Iterator)
&& let Some(into_iter_trait) = cx.tcx.get_diagnostic_item(sym::IntoIterator)
- && let Some(iter_item_ty) = make_normalized_projection(cx.tcx, cx.param_env, iter_trait, item, [iter_ty])
- && let Some(into_iter_item_proj) = make_projection(cx.tcx, into_iter_trait, item, [collect_ty])
+ && let Some(iter_item_ty) = make_normalized_projection(cx.tcx, cx.param_env, iter_trait, sym::Item, [iter_ty])
+ && let Some(into_iter_item_proj) = make_projection(cx.tcx, into_iter_trait, sym::Item, [collect_ty])
&& let Ok(into_iter_item_ty) = cx.tcx.try_normalize_erasing_regions(
cx.param_env,
- Ty::new_projection(cx.tcx,into_iter_item_proj.def_id, into_iter_item_proj.args)
+ Ty::new_projection(cx.tcx, into_iter_item_proj.def_id, into_iter_item_proj.args),
)
{
iter_item_ty == into_iter_item_ty
@@ -233,11 +227,14 @@ fn is_contains_sig(cx: &LateContext<'_>, call_id: HirId, iter_expr: &Expr<'_>) -
&& let [_, search_ty] = *sig.skip_binder().inputs()
&& let ty::Ref(_, search_ty, Mutability::Not) = *cx.tcx.erase_late_bound_regions(sig.rebind(search_ty)).kind()
&& let Some(iter_trait) = cx.tcx.get_diagnostic_item(sym::Iterator)
- && let Some(iter_item) = cx.tcx
- .associated_items(iter_trait)
- .find_by_name_and_kind(cx.tcx, Ident::with_dummy_span(Symbol::intern("Item")), AssocKind::Type, iter_trait)
+ && let Some(iter_item) = cx.tcx.associated_items(iter_trait).find_by_name_and_kind(
+ cx.tcx,
+ Ident::with_dummy_span(sym::Item),
+ AssocKind::Type,
+ iter_trait,
+ )
&& let args = cx.tcx.mk_args(&[GenericArg::from(typeck.expr_ty_adjusted(iter_expr))])
- && let proj_ty = Ty::new_projection(cx.tcx,iter_item.def_id, args)
+ && let proj_ty = Ty::new_projection(cx.tcx, iter_item.def_id, args)
&& let Ok(item_ty) = cx.tcx.try_normalize_erasing_regions(cx.param_env, proj_ty)
{
item_ty == EarlyBinder::bind(search_ty).instantiate(cx.tcx, cx.typeck_results().node_args(call_id))
diff --git a/src/tools/clippy/clippy_lints/src/methods/open_options.rs b/src/tools/clippy/clippy_lints/src/methods/open_options.rs
index 1c664e76d..65c986dca 100644
--- a/src/tools/clippy/clippy_lints/src/methods/open_options.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/open_options.rs
@@ -1,17 +1,17 @@
use clippy_utils::diagnostics::span_lint;
-use clippy_utils::paths;
-use clippy_utils::ty::match_type;
+use clippy_utils::ty::is_type_diagnostic_item;
use rustc_ast::ast::LitKind;
use rustc_hir::{Expr, ExprKind};
use rustc_lint::LateContext;
-use rustc_span::source_map::{Span, Spanned};
+use rustc_span::source_map::Spanned;
+use rustc_span::{sym, Span};
use super::NONSENSICAL_OPEN_OPTIONS;
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, recv: &'tcx Expr<'_>) {
if let Some(method_id) = cx.typeck_results().type_dependent_def_id(e.hir_id)
&& let Some(impl_id) = cx.tcx.impl_of_method(method_id)
- && match_type(cx, cx.tcx.type_of(impl_id).instantiate_identity(), &paths::OPEN_OPTIONS)
+ && is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id).instantiate_identity(), sym::FsOpenOptions)
{
let mut options = Vec::new();
get_open_options(cx, recv, &mut options);
@@ -40,7 +40,7 @@ fn get_open_options(cx: &LateContext<'_>, argument: &Expr<'_>, options: &mut Vec
let obj_ty = cx.typeck_results().expr_ty(receiver).peel_refs();
// Only proceed if this is a call on some object of type std::fs::OpenOptions
- if match_type(cx, obj_ty, &paths::OPEN_OPTIONS) && !arguments.is_empty() {
+ if is_type_diagnostic_item(cx, obj_ty, sym::FsOpenOptions) && !arguments.is_empty() {
let argument_option = match arguments[0].kind {
ExprKind::Lit(span) => {
if let Spanned {
diff --git a/src/tools/clippy/clippy_lints/src/methods/option_as_ref_deref.rs b/src/tools/clippy/clippy_lints/src/methods/option_as_ref_deref.rs
index 3e33f9193..7b81d4571 100644
--- a/src/tools/clippy/clippy_lints/src/methods/option_as_ref_deref.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/option_as_ref_deref.rs
@@ -1,5 +1,5 @@
+use clippy_config::msrvs::{self, Msrv};
use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::source::snippet;
use clippy_utils::ty::is_type_diagnostic_item;
use clippy_utils::{match_def_path, path_to_local_id, paths, peel_blocks};
@@ -32,8 +32,7 @@ pub(super) fn check(
return;
}
- let deref_aliases: [&[&str]; 8] = [
- &paths::DEREF_MUT_TRAIT_METHOD,
+ let deref_aliases: [&[&str]; 7] = [
&paths::CSTRING_AS_C_STR,
&paths::OS_STRING_AS_OS_STR,
&paths::PATH_BUF_AS_PATH,
@@ -49,6 +48,7 @@ pub(super) fn check(
.opt_def_id()
.map_or(false, |fun_def_id| {
cx.tcx.is_diagnostic_item(sym::deref_method, fun_def_id)
+ || cx.tcx.is_diagnostic_item(sym::deref_mut_method, fun_def_id)
|| deref_aliases.iter().any(|path| match_def_path(cx, fun_def_id, path))
})
},
@@ -70,6 +70,7 @@ pub(super) fn check(
then {
let method_did = cx.typeck_results().type_dependent_def_id(closure_expr.hir_id).unwrap();
cx.tcx.is_diagnostic_item(sym::deref_method, method_did)
+ || cx.tcx.is_diagnostic_item(sym::deref_mut_method, method_did)
|| deref_aliases.iter().any(|path| match_def_path(cx, method_did, path))
} else {
false
diff --git a/src/tools/clippy/clippy_lints/src/methods/option_map_unwrap_or.rs b/src/tools/clippy/clippy_lints/src/methods/option_map_unwrap_or.rs
index fcbe005fb..c78f8b71c 100644
--- a/src/tools/clippy/clippy_lints/src/methods/option_map_unwrap_or.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/option_map_unwrap_or.rs
@@ -1,5 +1,5 @@
+use clippy_config::msrvs::{self, Msrv};
use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::ty::{is_copy, is_type_diagnostic_item};
use rustc_data_structures::fx::FxHashSet;
@@ -9,8 +9,7 @@ use rustc_hir::intravisit::{walk_path, Visitor};
use rustc_hir::{self, ExprKind, HirId, Node, PatKind, Path, QPath};
use rustc_lint::LateContext;
use rustc_middle::hir::nested_filter;
-use rustc_span::source_map::Span;
-use rustc_span::sym;
+use rustc_span::{sym, Span};
use super::MAP_UNWRAP_OR;
diff --git a/src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs b/src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs
index 942f3bd79..b89c15146 100644
--- a/src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs
@@ -7,7 +7,7 @@ use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_lint::LateContext;
use rustc_middle::ty;
-use rustc_span::source_map::Span;
+use rustc_span::Span;
use rustc_span::symbol::{self, sym, Symbol};
use {rustc_ast as ast, rustc_hir as hir};
@@ -235,10 +235,10 @@ fn closure_body_returns_empty_to_string(cx: &LateContext<'_>, e: &hir::Expr<'_>)
let body = cx.tcx.hir().body(body);
if body.params.is_empty()
- && let hir::Expr{ kind, .. } = &body.value
- && let hir::ExprKind::MethodCall(hir::PathSegment {ident, ..}, self_arg, _, _) = kind
+ && let hir::Expr { kind, .. } = &body.value
+ && let hir::ExprKind::MethodCall(hir::PathSegment { ident, .. }, self_arg, _, _) = kind
&& ident.name == sym::to_string
- && let hir::Expr{ kind, .. } = self_arg
+ && let hir::Expr { kind, .. } = self_arg
&& let hir::ExprKind::Lit(lit) = kind
&& let ast::LitKind::Str(symbol::kw::Empty, _) = lit.node
{
diff --git a/src/tools/clippy/clippy_lints/src/methods/path_ends_with_ext.rs b/src/tools/clippy/clippy_lints/src/methods/path_ends_with_ext.rs
index 3347c8c16..094ead9f4 100644
--- a/src/tools/clippy/clippy_lints/src/methods/path_ends_with_ext.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/path_ends_with_ext.rs
@@ -1,7 +1,6 @@
use super::PATH_ENDS_WITH_EXT;
+use clippy_config::msrvs::{self, Msrv};
use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::msrvs;
-use clippy_utils::msrvs::Msrv;
use clippy_utils::source::snippet;
use clippy_utils::ty::is_type_diagnostic_item;
use rustc_ast::{LitKind, StrStyle};
@@ -47,7 +46,7 @@ pub(super) fn check(
"this looks like a failed attempt at checking for the file extension",
"try",
sugg,
- Applicability::MaybeIncorrect
+ Applicability::MaybeIncorrect,
);
}
}
diff --git a/src/tools/clippy/clippy_lints/src/methods/read_line_without_trim.rs b/src/tools/clippy/clippy_lints/src/methods/read_line_without_trim.rs
index 81f9e2a77..3b903ec5c 100644
--- a/src/tools/clippy/clippy_lints/src/methods/read_line_without_trim.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/read_line_without_trim.rs
@@ -46,9 +46,12 @@ pub fn check(cx: &LateContext<'_>, call: &Expr<'_>, recv: &Expr<'_>, arg: &Expr<
span,
"calling `.parse()` without trimming the trailing newline character",
|diag| {
- diag.span_note(call.span, "call to `.read_line()` here, \
+ diag.span_note(
+ call.span,
+ "call to `.read_line()` here, \
which leaves a trailing newline character in the buffer, \
- which in turn will cause `.parse()` to fail");
+ which in turn will cause `.parse()` to fail",
+ );
diag.span_suggestion(
expr.span,
@@ -56,7 +59,7 @@ pub fn check(cx: &LateContext<'_>, call: &Expr<'_>, recv: &Expr<'_>, arg: &Expr<
format!("{local_snippet}.trim_end()"),
Applicability::MachineApplicable,
);
- }
+ },
);
}
diff --git a/src/tools/clippy/clippy_lints/src/methods/readonly_write_lock.rs b/src/tools/clippy/clippy_lints/src/methods/readonly_write_lock.rs
index e3ec921da..1184dd452 100644
--- a/src/tools/clippy/clippy_lints/src/methods/readonly_write_lock.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/readonly_write_lock.rs
@@ -26,13 +26,18 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, receiver
&& let parent = cx.tcx.hir().get_parent(unwrap_call_expr.hir_id)
&& let Node::Local(local) = parent
&& let Some(mir) = enclosing_mir(cx.tcx, expr.hir_id)
- && let Some((local, _)) = mir.local_decls.iter_enumerated().find(|(_, decl)| {
- local.span.contains(decl.source_info.span)
- })
- && let Some(usages) = visit_local_usage(&[local], mir, Location {
- block: START_BLOCK,
- statement_index: 0,
- })
+ && let Some((local, _)) = mir
+ .local_decls
+ .iter_enumerated()
+ .find(|(_, decl)| local.span.contains(decl.source_info.span))
+ && let Some(usages) = visit_local_usage(
+ &[local],
+ mir,
+ Location {
+ block: START_BLOCK,
+ statement_index: 0,
+ },
+ )
&& let [usage] = usages.as_slice()
{
let writer_never_mutated = usage.local_consume_or_mutate_locs.is_empty();
@@ -45,7 +50,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, receiver
"this write lock is used only for reading",
"consider using a read lock instead",
format!("{}.read()", snippet(cx, receiver.span, "<receiver>")),
- Applicability::MaybeIncorrect // write lock might be intentional for enforcing exclusiveness
+ Applicability::MaybeIncorrect, // write lock might be intentional for enforcing exclusiveness
);
}
}
diff --git a/src/tools/clippy/clippy_lints/src/methods/search_is_some.rs b/src/tools/clippy/clippy_lints/src/methods/search_is_some.rs
index afdb8ce94..05a9a06c8 100644
--- a/src/tools/clippy/clippy_lints/src/methods/search_is_some.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/search_is_some.rs
@@ -8,7 +8,7 @@ use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_hir::PatKind;
use rustc_lint::LateContext;
-use rustc_span::source_map::Span;
+use rustc_span::Span;
use rustc_span::symbol::sym;
use super::SEARCH_IS_SOME;
@@ -39,7 +39,7 @@ pub(super) fn check<'tcx>(
if search_method == "find";
if let hir::ExprKind::Closure(&hir::Closure { body, .. }) = search_arg.kind;
let closure_body = cx.tcx.hir().body(body);
- if let Some(closure_arg) = closure_body.params.get(0);
+ if let Some(closure_arg) = closure_body.params.first();
then {
if let hir::PatKind::Ref(..) = closure_arg.pat.kind {
Some(search_snippet.replacen('&', "", 1))
diff --git a/src/tools/clippy/clippy_lints/src/methods/seek_from_current.rs b/src/tools/clippy/clippy_lints/src/methods/seek_from_current.rs
index f3d6a15ed..63d41677f 100644
--- a/src/tools/clippy/clippy_lints/src/methods/seek_from_current.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/seek_from_current.rs
@@ -2,18 +2,19 @@ use rustc_ast::ast::{LitIntType, LitKind};
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind};
use rustc_lint::LateContext;
+use rustc_span::sym;
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::ty::implements_trait;
-use clippy_utils::{get_trait_def_id, match_def_path, paths};
+use clippy_utils::{match_def_path, paths};
use super::SEEK_FROM_CURRENT;
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, recv: &'tcx Expr<'_>, arg: &'tcx Expr<'_>) {
let ty = cx.typeck_results().expr_ty(recv);
- if let Some(def_id) = get_trait_def_id(cx, &paths::STD_IO_SEEK) {
+ if let Some(def_id) = cx.tcx.get_diagnostic_item(sym::IoSeek) {
if implements_trait(cx, ty, def_id, &[]) && arg_is_seek_from_current(cx, arg) {
let mut applicability = Applicability::MachineApplicable;
let snip = snippet_with_applicability(cx, recv.span, "..", &mut applicability);
@@ -32,15 +33,17 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, recv: &'
}
fn arg_is_seek_from_current<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
- if let ExprKind::Call(f, args) = expr.kind &&
- let ExprKind::Path(ref path) = f.kind &&
- let Some(def_id) = cx.qpath_res(path, f.hir_id).opt_def_id() &&
- match_def_path(cx, def_id, &paths::STD_IO_SEEK_FROM_CURRENT) {
+ if let ExprKind::Call(f, args) = expr.kind
+ && let ExprKind::Path(ref path) = f.kind
+ && let Some(def_id) = cx.qpath_res(path, f.hir_id).opt_def_id()
+ && match_def_path(cx, def_id, &paths::STD_IO_SEEK_FROM_CURRENT)
+ {
// check if argument of `SeekFrom::Current` is `0`
- if args.len() == 1 &&
- let ExprKind::Lit(lit) = args[0].kind &&
- let LitKind::Int(0, LitIntType::Unsuffixed) = lit.node {
- return true
+ if args.len() == 1
+ && let ExprKind::Lit(lit) = args[0].kind
+ && let LitKind::Int(0, LitIntType::Unsuffixed) = lit.node
+ {
+ return true;
}
}
diff --git a/src/tools/clippy/clippy_lints/src/methods/seek_to_start_instead_of_rewind.rs b/src/tools/clippy/clippy_lints/src/methods/seek_to_start_instead_of_rewind.rs
index 787e9e0eb..9f3846035 100644
--- a/src/tools/clippy/clippy_lints/src/methods/seek_to_start_instead_of_rewind.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/seek_to_start_instead_of_rewind.rs
@@ -1,11 +1,11 @@
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::ty::implements_trait;
-use clippy_utils::{get_trait_def_id, is_expr_used_or_unified, match_def_path, paths};
+use clippy_utils::{is_expr_used_or_unified, match_def_path, paths};
use rustc_ast::ast::{LitIntType, LitKind};
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind};
use rustc_lint::LateContext;
-use rustc_span::Span;
+use rustc_span::{sym, Span};
use super::SEEK_TO_START_INSTEAD_OF_REWIND;
@@ -23,15 +23,15 @@ pub(super) fn check<'tcx>(
return;
}
- if let Some(seek_trait_id) = get_trait_def_id(cx, &paths::STD_IO_SEEK) &&
- implements_trait(cx, ty, seek_trait_id, &[]) &&
- let ExprKind::Call(func, args1) = arg.kind &&
- let ExprKind::Path(ref path) = func.kind &&
- let Some(def_id) = cx.qpath_res(path, func.hir_id).opt_def_id() &&
- match_def_path(cx, def_id, &paths::STD_IO_SEEKFROM_START) &&
- args1.len() == 1 &&
- let ExprKind::Lit(lit) = args1[0].kind &&
- let LitKind::Int(0, LitIntType::Unsuffixed) = lit.node
+ if let Some(seek_trait_id) = cx.tcx.get_diagnostic_item(sym::IoSeek)
+ && implements_trait(cx, ty, seek_trait_id, &[])
+ && let ExprKind::Call(func, args1) = arg.kind
+ && let ExprKind::Path(ref path) = func.kind
+ && let Some(def_id) = cx.qpath_res(path, func.hir_id).opt_def_id()
+ && match_def_path(cx, def_id, &paths::STD_IO_SEEKFROM_START)
+ && args1.len() == 1
+ && let ExprKind::Lit(lit) = args1[0].kind
+ && let LitKind::Int(0, LitIntType::Unsuffixed) = lit.node
{
let method_call_span = expr.span.with_lo(name_span.lo());
span_lint_and_then(
diff --git a/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs b/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs
index 7016ad0a8..9da61bca5 100644
--- a/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs
@@ -1,6 +1,6 @@
+use clippy_config::msrvs::{self, Msrv};
use clippy_utils::consts::{constant, Constant};
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
-use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::source::snippet_with_context;
use clippy_utils::usage::local_used_after_expr;
use clippy_utils::visitors::{for_each_expr_with_closures, Descend};
@@ -115,7 +115,7 @@ fn check_manual_split_once(
/// checks for
///
-/// ```
+/// ```no_run
/// let mut iter = "a.b.c".splitn(2, '.');
/// let a = iter.next();
/// let b = iter.next();
@@ -133,13 +133,11 @@ fn check_manual_split_once_indirect(
&& let PatKind::Binding(BindingAnnotation::MUT, iter_binding_id, iter_ident, None) = local.pat.kind
&& let (iter_stmt_id, Node::Stmt(_)) = parents.next()?
&& let (_, Node::Block(enclosing_block)) = parents.next()?
-
&& let mut stmts = enclosing_block
.stmts
.iter()
.skip_while(|stmt| stmt.hir_id != iter_stmt_id)
.skip(1)
-
&& let first = indirect_usage(cx, stmts.next()?, iter_binding_id, ctxt)?
&& let second = indirect_usage(cx, stmts.next()?, iter_binding_id, ctxt)?
&& first.unwrap_kind == second.unwrap_kind
@@ -173,18 +171,8 @@ fn check_manual_split_once_indirect(
);
let remove_msg = format!("remove the `{iter_ident}` usages");
- diag.span_suggestion(
- first.span,
- remove_msg.clone(),
- "",
- app,
- );
- diag.span_suggestion(
- second.span,
- remove_msg,
- "",
- app,
- );
+ diag.span_suggestion(first.span, remove_msg.clone(), "", app);
+ diag.span_suggestion(second.span, remove_msg, "", app);
});
}
diff --git a/src/tools/clippy/clippy_lints/src/methods/string_lit_chars_any.rs b/src/tools/clippy/clippy_lints/src/methods/string_lit_chars_any.rs
index 70da6ad58..5f6f027a3 100644
--- a/src/tools/clippy/clippy_lints/src/methods/string_lit_chars_any.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/string_lit_chars_any.rs
@@ -1,5 +1,5 @@
+use clippy_config::msrvs::{self, Msrv};
use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::msrvs::{Msrv, MATCHES_MACRO};
use clippy_utils::source::snippet_opt;
use clippy_utils::{is_from_proc_macro, is_trait_method, path_to_local};
use itertools::Itertools;
@@ -19,7 +19,7 @@ pub(super) fn check<'tcx>(
body: &Expr<'_>,
msrv: &Msrv,
) {
- if msrv.meets(MATCHES_MACRO)
+ if msrv.meets(msrvs::MATCHES_MACRO)
&& is_trait_method(cx, expr, sym::Iterator)
&& let PatKind::Binding(_, arg, _, _) = param.pat.kind
&& let ExprKind::Lit(lit_kind) = recv.kind
@@ -52,7 +52,7 @@ pub(super) fn check<'tcx>(
format!("matches!({scrutinee_snip}, {pat_snip})"),
Applicability::MachineApplicable,
);
- }
+ },
);
}
}
diff --git a/src/tools/clippy/clippy_lints/src/methods/suspicious_command_arg_space.rs b/src/tools/clippy/clippy_lints/src/methods/suspicious_command_arg_space.rs
index bc8f01767..b2c5987e4 100644
--- a/src/tools/clippy/clippy_lints/src/methods/suspicious_command_arg_space.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/suspicious_command_arg_space.rs
@@ -1,9 +1,8 @@
use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::paths;
-use clippy_utils::ty::match_type;
+use clippy_utils::ty::is_type_diagnostic_item;
use rustc_errors::{Applicability, Diagnostic};
use rustc_lint::LateContext;
-use rustc_span::Span;
+use rustc_span::{sym, Span};
use {rustc_ast as ast, rustc_hir as hir};
use super::SUSPICIOUS_COMMAND_ARG_SPACE;
@@ -11,7 +10,7 @@ use super::SUSPICIOUS_COMMAND_ARG_SPACE;
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, recv: &'tcx hir::Expr<'_>, arg: &'tcx hir::Expr<'_>, span: Span) {
let ty = cx.typeck_results().expr_ty(recv).peel_refs();
- if match_type(cx, ty, &paths::STD_PROCESS_COMMAND)
+ if is_type_diagnostic_item(cx, ty, sym::Command)
&& let hir::ExprKind::Lit(lit) = &arg.kind
&& let ast::LitKind::Str(s, _) = &lit.node
&& let Some((arg1, arg2)) = s.as_str().split_once(' ')
@@ -26,13 +25,10 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, recv: &'tcx hir::Expr<'_>, arg
|diag: &mut Diagnostic| {
diag.multipart_suggestion_verbose(
"consider splitting the argument",
- vec![
- (span, "args".to_string()),
- (arg.span, format!("[{arg1:?}, {arg2:?}]")),
- ],
+ vec![(span, "args".to_string()), (arg.span, format!("[{arg1:?}, {arg2:?}]"))],
Applicability::MaybeIncorrect,
);
- }
+ },
);
}
}
diff --git a/src/tools/clippy/clippy_lints/src/methods/type_id_on_box.rs b/src/tools/clippy/clippy_lints/src/methods/type_id_on_box.rs
index 3404bdfe7..4917936a9 100644
--- a/src/tools/clippy/clippy_lints/src/methods/type_id_on_box.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/type_id_on_box.rs
@@ -44,11 +44,11 @@ pub(super) fn check(cx: &LateContext<'_>, receiver: &Expr<'_>, call_span: Span)
diag.note(
"this returns the type id of the literal type `Box<dyn Any>` instead of the \
- type id of the boxed value, which is most likely not what you want"
+ type id of the boxed value, which is most likely not what you want",
)
.note(
"if this is intentional, use `TypeId::of::<Box<dyn Any>>()` instead, \
- which makes it more clear"
+ which makes it more clear",
)
.span_suggestion(
receiver.span,
diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_fallible_conversions.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_fallible_conversions.rs
new file mode 100644
index 000000000..bb32b1bb7
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_fallible_conversions.rs
@@ -0,0 +1,122 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::get_parent_expr;
+use clippy_utils::ty::implements_trait;
+use rustc_errors::Applicability;
+use rustc_hir::{Expr, ExprKind};
+use rustc_lint::LateContext;
+use rustc_middle::ty;
+use rustc_span::{sym, Span};
+
+use super::UNNECESSARY_FALLIBLE_CONVERSIONS;
+
+/// What function is being called and whether that call is written as a method call or a function
+/// call
+#[derive(Copy, Clone)]
+#[expect(clippy::enum_variant_names)]
+enum FunctionKind {
+ /// `T::try_from(U)`
+ TryFromFunction,
+ /// `t.try_into()`
+ TryIntoMethod,
+ /// `U::try_into(t)`
+ TryIntoFunction,
+}
+
+fn check<'tcx>(
+ cx: &LateContext<'tcx>,
+ expr: &Expr<'_>,
+ node_args: ty::GenericArgsRef<'tcx>,
+ kind: FunctionKind,
+ primary_span: Span,
+) {
+ if let &[self_ty, other_ty] = node_args.as_slice()
+ // useless_conversion already warns `T::try_from(T)`, so ignore it here
+ && self_ty != other_ty
+ && let Some(self_ty) = self_ty.as_type()
+ && let Some(from_into_trait) = cx.tcx.get_diagnostic_item(match kind {
+ FunctionKind::TryFromFunction => sym::From,
+ FunctionKind::TryIntoMethod | FunctionKind::TryIntoFunction => sym::Into,
+ })
+ // If `T: TryFrom<U>` and `T: From<U>` both exist, then that means that the `TryFrom`
+ // _must_ be from the blanket impl and cannot have been manually implemented
+ // (else there would be conflicting impls, even with #![feature(spec)]), so we don't even need to check
+ // what `<T as TryFrom<U>>::Error` is: it's always `Infallible`
+ && implements_trait(cx, self_ty, from_into_trait, &[other_ty])
+ {
+ let parent_unwrap_call = get_parent_expr(cx, expr).and_then(|parent| {
+ if let ExprKind::MethodCall(path, .., span) = parent.kind
+ && let sym::unwrap | sym::expect = path.ident.name
+ {
+ Some(span)
+ } else {
+ None
+ }
+ });
+
+ let (sugg, span, applicability) = match kind {
+ FunctionKind::TryIntoMethod if let Some(unwrap_span) = parent_unwrap_call => {
+ // Extend the span to include the unwrap/expect call:
+ // `foo.try_into().expect("..")`
+ // ^^^^^^^^^^^^^^^^^^^^^^^
+ //
+ // `try_into().unwrap()` specifically can be trivially replaced with just `into()`,
+ // so that can be machine-applicable
+
+ (
+ "into()",
+ primary_span.with_hi(unwrap_span.hi()),
+ Applicability::MachineApplicable,
+ )
+ },
+ FunctionKind::TryFromFunction => ("From::from", primary_span, Applicability::Unspecified),
+ FunctionKind::TryIntoFunction => ("Into::into", primary_span, Applicability::Unspecified),
+ FunctionKind::TryIntoMethod => ("into", primary_span, Applicability::Unspecified),
+ };
+
+ span_lint_and_sugg(
+ cx,
+ UNNECESSARY_FALLIBLE_CONVERSIONS,
+ span,
+ "use of a fallible conversion when an infallible one could be used",
+ "use",
+ sugg.into(),
+ applicability,
+ );
+ }
+}
+
+/// Checks method call exprs:
+/// - `0i32.try_into()`
+pub(super) fn check_method(cx: &LateContext<'_>, expr: &Expr<'_>) {
+ if let ExprKind::MethodCall(path, ..) = expr.kind {
+ check(
+ cx,
+ expr,
+ cx.typeck_results().node_args(expr.hir_id),
+ FunctionKind::TryIntoMethod,
+ path.ident.span,
+ );
+ }
+}
+
+/// Checks function call exprs:
+/// - `<i64 as TryFrom<_>>::try_from(0i32)`
+/// - `<_ as TryInto<i64>>::try_into(0i32)`
+pub(super) fn check_function(cx: &LateContext<'_>, expr: &Expr<'_>, callee: &Expr<'_>) {
+ if let ExprKind::Path(ref qpath) = callee.kind
+ && let Some(item_def_id) = cx.qpath_res(qpath, callee.hir_id).opt_def_id()
+ && let Some(trait_def_id) = cx.tcx.trait_of_item(item_def_id)
+ {
+ check(
+ cx,
+ expr,
+ cx.typeck_results().node_args(callee.hir_id),
+ match cx.tcx.get_diagnostic_name(trait_def_id) {
+ Some(sym::TryFrom) => FunctionKind::TryFromFunction,
+ Some(sym::TryInto) => FunctionKind::TryIntoFunction,
+ _ => return,
+ },
+ callee.span,
+ );
+ }
+}
diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_fold.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_fold.rs
index 6e23754bf..6d51c4ab0 100644
--- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_fold.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_fold.rs
@@ -8,8 +8,7 @@ use rustc_hir as hir;
use rustc_hir::PatKind;
use rustc_lint::LateContext;
use rustc_middle::ty;
-use rustc_span::source_map::Span;
-use rustc_span::sym;
+use rustc_span::{sym, Span};
use super::UNNECESSARY_FOLD;
diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_lazy_eval.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_lazy_eval.rs
index 47e2e7441..4429f0326 100644
--- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_lazy_eval.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_lazy_eval.rs
@@ -2,6 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::source::snippet;
use clippy_utils::ty::is_type_diagnostic_item;
use clippy_utils::{eager_or_lazy, is_from_proc_macro, usage};
+use hir::FnRetTy;
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::LateContext;
@@ -18,20 +19,16 @@ pub(super) fn check<'tcx>(
arg: &'tcx hir::Expr<'_>,
simplify_using: &str,
) {
- if is_from_proc_macro(cx, expr) {
- return;
- }
-
let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Option);
let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result);
let is_bool = cx.typeck_results().expr_ty(recv).is_bool();
if is_option || is_result || is_bool {
- if let hir::ExprKind::Closure(&hir::Closure { body, .. }) = arg.kind {
+ if let hir::ExprKind::Closure(&hir::Closure { body, fn_decl, .. }) = arg.kind {
let body = cx.tcx.hir().body(body);
let body_expr = &body.value;
- if usage::BindingUsageFinder::are_params_used(cx, body) {
+ if usage::BindingUsageFinder::are_params_used(cx, body) || is_from_proc_macro(cx, expr) {
return;
}
@@ -48,7 +45,14 @@ pub(super) fn check<'tcx>(
.iter()
// bindings are checked to be unused above
.all(|param| matches!(param.pat.kind, hir::PatKind::Binding(..) | hir::PatKind::Wild))
- {
+ && matches!(
+ fn_decl.output,
+ FnRetTy::DefaultReturn(_)
+ | FnRetTy::Return(hir::Ty {
+ kind: hir::TyKind::Infer,
+ ..
+ })
+ ) {
Applicability::MachineApplicable
} else {
// replacing the lambda may break type inference
diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_literal_unwrap.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_literal_unwrap.rs
index 937aac8d2..a1125d70d 100644
--- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_literal_unwrap.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_literal_unwrap.rs
@@ -24,7 +24,6 @@ fn get_ty_from_args<'a>(args: Option<&'a [hir::GenericArg<'a>]>, index: usize) -
}
}
-#[expect(clippy::too_many_lines)]
pub(super) fn check(
cx: &LateContext<'_>,
expr: &hir::Expr<'_>,
@@ -82,7 +81,7 @@ pub(super) fn check(
{
suggs.extend([
(block.span.shrink_to_lo().to(expr.span.shrink_to_lo()), String::new()),
- (expr.span.shrink_to_hi().to(block.span.shrink_to_hi()), String::new())
+ (expr.span.shrink_to_hi().to(block.span.shrink_to_hi()), String::new()),
]);
}
Some(suggs)
@@ -101,15 +100,11 @@ pub(super) fn check(
(expr.span.with_lo(args[0].span.hi()), String::new()),
]),
("None", "unwrap_or_else", _) => match args[0].kind {
- hir::ExprKind::Closure(hir::Closure {
- fn_decl:
- hir::FnDecl {
- output: hir::FnRetTy::DefaultReturn(span) | hir::FnRetTy::Return(hir::Ty { span, .. }),
- ..
- },
- ..
- }) => Some(vec![
- (expr.span.with_hi(span.hi()), String::new()),
+ hir::ExprKind::Closure(hir::Closure { body, .. }) => Some(vec![
+ (
+ expr.span.with_hi(cx.tcx.hir().body(*body).value.span.lo()),
+ String::new(),
+ ),
(expr.span.with_lo(args[0].span.hi()), String::new()),
]),
_ => None,
diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs
index 50d6f3b7e..7a50feff6 100644
--- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs
@@ -1,7 +1,7 @@
use super::implicit_clone::is_clone_like;
use super::unnecessary_iter_cloned::{self, is_into_iter};
+use clippy_config::msrvs::{self, Msrv};
use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::source::snippet_opt;
use clippy_utils::ty::{get_iterator_item_ty, implements_trait, is_copy, peel_mid_ty_refs};
use clippy_utils::visitors::find_all_ret_expressions;
@@ -15,8 +15,7 @@ use rustc_lint::LateContext;
use rustc_middle::mir::Mutability;
use rustc_middle::ty::adjustment::{Adjust, Adjustment, OverloadedDeref};
use rustc_middle::ty::{
- self, ClauseKind, EarlyBinder, GenericArg, GenericArgKind, GenericArgsRef, ParamTy, ProjectionPredicate,
- TraitPredicate, Ty,
+ self, ClauseKind, GenericArg, GenericArgKind, GenericArgsRef, ParamTy, ProjectionPredicate, TraitPredicate, Ty,
};
use rustc_span::{sym, Symbol};
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
@@ -375,6 +374,7 @@ fn get_input_traits_and_projections<'tcx>(
(trait_predicates, projection_predicates)
}
+#[expect(clippy::too_many_lines)]
fn can_change_type<'a>(cx: &LateContext<'a>, mut expr: &'a Expr<'a>, mut ty: Ty<'a>) -> bool {
for (_, node) in cx.tcx.hir().parent_iter(expr.hir_id) {
match node {
@@ -382,10 +382,10 @@ fn can_change_type<'a>(cx: &LateContext<'a>, mut expr: &'a Expr<'a>, mut ty: Ty<
Node::Block(..) => continue,
Node::Item(item) => {
if let ItemKind::Fn(_, _, body_id) = &item.kind
- && let output_ty = return_ty(cx, item.owner_id)
- && let inherited = Inherited::new(cx.tcx, item.owner_id.def_id)
- && let fn_ctxt = FnCtxt::new(&inherited, cx.param_env, item.owner_id.def_id)
- && fn_ctxt.can_coerce(ty, output_ty)
+ && let output_ty = return_ty(cx, item.owner_id)
+ && let inherited = Inherited::new(cx.tcx, item.owner_id.def_id)
+ && let fn_ctxt = FnCtxt::new(&inherited, cx.param_env, item.owner_id.def_id)
+ && fn_ctxt.can_coerce(ty, output_ty)
{
if has_lifetime(output_ty) && has_lifetime(ty) {
return false;
@@ -393,67 +393,78 @@ fn can_change_type<'a>(cx: &LateContext<'a>, mut expr: &'a Expr<'a>, mut ty: Ty<
let body = cx.tcx.hir().body(*body_id);
let body_expr = &body.value;
let mut count = 0;
- return find_all_ret_expressions(cx, body_expr, |_| { count += 1; count <= 1 });
+ return find_all_ret_expressions(cx, body_expr, |_| {
+ count += 1;
+ count <= 1
+ });
}
- }
+ },
Node::Expr(parent_expr) => {
- if let Some((callee_def_id, call_generic_args, recv, call_args))
- = get_callee_generic_args_and_args(cx, parent_expr)
+ if let Some((callee_def_id, call_generic_args, recv, call_args)) =
+ get_callee_generic_args_and_args(cx, parent_expr)
{
- // FIXME: the `instantiate_identity()` below seems incorrect, since we eventually
- // call `tcx.try_instantiate_and_normalize_erasing_regions` further down
- // (i.e., we are explicitly not in the identity context).
- let fn_sig = cx.tcx.fn_sig(callee_def_id).instantiate_identity().skip_binder();
+ let bound_fn_sig = cx.tcx.fn_sig(callee_def_id);
+ let fn_sig = bound_fn_sig.skip_binder();
if let Some(arg_index) = recv.into_iter().chain(call_args).position(|arg| arg.hir_id == expr.hir_id)
- && let Some(param_ty) = fn_sig.inputs().get(arg_index)
- && let ty::Param(ParamTy { index: param_index , ..}) = param_ty.kind()
+ && let param_ty = fn_sig.input(arg_index).skip_binder()
+ && let ty::Param(ParamTy { index: param_index , ..}) = *param_ty.kind()
// https://github.com/rust-lang/rust-clippy/issues/9504 and https://github.com/rust-lang/rust-clippy/issues/10021
- && (*param_index as usize) < call_generic_args.len()
+ && (param_index as usize) < call_generic_args.len()
{
if fn_sig
+ .skip_binder()
.inputs()
.iter()
.enumerate()
.filter(|(i, _)| *i != arg_index)
- .any(|(_, ty)| ty.contains(*param_ty))
+ .any(|(_, ty)| ty.contains(param_ty))
{
return false;
}
- let mut trait_predicates = cx.tcx.param_env(callee_def_id)
- .caller_bounds().iter().filter(|predicate| {
- if let ClauseKind::Trait(trait_predicate)
- = predicate.kind().skip_binder()
- && trait_predicate.trait_ref.self_ty() == *param_ty
- {
- true
- } else {
- false
- }
- });
+ let mut trait_predicates =
+ cx.tcx
+ .param_env(callee_def_id)
+ .caller_bounds()
+ .iter()
+ .filter(|predicate| {
+ if let ClauseKind::Trait(trait_predicate) = predicate.kind().skip_binder()
+ && trait_predicate.trait_ref.self_ty() == param_ty
+ {
+ true
+ } else {
+ false
+ }
+ });
- let new_subst = cx.tcx.mk_args_from_iter(
- call_generic_args.iter()
- .enumerate()
- .map(|(i, t)|
- if i == (*param_index as usize) {
- GenericArg::from(ty)
- } else {
- t
- }));
+ let new_subst = cx
+ .tcx
+ .mk_args_from_iter(call_generic_args.iter().enumerate().map(|(i, t)| {
+ if i == param_index as usize {
+ GenericArg::from(ty)
+ } else {
+ t
+ }
+ }));
if trait_predicates.any(|predicate| {
- let predicate = EarlyBinder::bind(predicate).instantiate(cx.tcx, new_subst);
+ let predicate = bound_fn_sig.rebind(predicate).instantiate(cx.tcx, new_subst);
let obligation = Obligation::new(cx.tcx, ObligationCause::dummy(), cx.param_env, predicate);
- !cx.tcx.infer_ctxt().build().predicate_must_hold_modulo_regions(&obligation)
+ !cx.tcx
+ .infer_ctxt()
+ .build()
+ .predicate_must_hold_modulo_regions(&obligation)
}) {
return false;
}
- let output_ty = fn_sig.output();
- if output_ty.contains(*param_ty) {
- if let Ok(new_ty) = cx.tcx.try_instantiate_and_normalize_erasing_regions(
- new_subst, cx.param_env, EarlyBinder::bind(output_ty)) {
+ let output_ty = cx.tcx.erase_late_bound_regions(fn_sig.output());
+ if output_ty.contains(param_ty) {
+ if let Ok(new_ty) = cx.tcx.try_instantiate_and_normalize_erasing_regions(
+ new_subst,
+ cx.param_env,
+ bound_fn_sig.rebind(output_ty),
+ ) {
expr = parent_expr;
ty = new_ty;
continue;
@@ -515,10 +526,11 @@ fn is_to_string_on_string_like<'a>(
&& let GenericArgKind::Type(ty) = generic_arg.unpack()
&& let Some(deref_trait_id) = cx.tcx.get_diagnostic_item(sym::Deref)
&& let Some(as_ref_trait_id) = cx.tcx.get_diagnostic_item(sym::AsRef)
- && (cx.get_associated_type(ty, deref_trait_id, "Target") == Some(cx.tcx.types.str_) ||
- implements_trait(cx, ty, as_ref_trait_id, &[cx.tcx.types.str_.into()])) {
- true
- } else {
- false
- }
+ && (cx.get_associated_type(ty, deref_trait_id, "Target") == Some(cx.tcx.types.str_)
+ || implements_trait(cx, ty, as_ref_trait_id, &[cx.tcx.types.str_.into()]))
+ {
+ true
+ } else {
+ false
+ }
}
diff --git a/src/tools/clippy/clippy_lints/src/methods/waker_clone_wake.rs b/src/tools/clippy/clippy_lints/src/methods/waker_clone_wake.rs
new file mode 100644
index 000000000..da66632d5
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/methods/waker_clone_wake.rs
@@ -0,0 +1,32 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::{is_trait_method, match_def_path, paths};
+use rustc_errors::Applicability;
+use rustc_hir::{Expr, ExprKind};
+use rustc_lint::LateContext;
+use rustc_span::sym;
+
+use super::WAKER_CLONE_WAKE;
+
+pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, recv: &'tcx Expr<'_>) {
+ let ty = cx.typeck_results().expr_ty(recv);
+
+ if let Some(did) = ty.ty_adt_def()
+ && match_def_path(cx, did.did(), &paths::WAKER)
+ && let ExprKind::MethodCall(_, waker_ref, &[], _) = recv.kind
+ && is_trait_method(cx, recv, sym::Clone)
+ {
+ let mut applicability = Applicability::MachineApplicable;
+ let snippet = snippet_with_applicability(cx, waker_ref.span.source_callsite(), "..", &mut applicability);
+
+ span_lint_and_sugg(
+ cx,
+ WAKER_CLONE_WAKE,
+ expr.span,
+ "cloning a `Waker` only to wake it",
+ "replace with",
+ format!("{snippet}.wake_by_ref()"),
+ applicability,
+ );
+ }
+}
diff --git a/src/tools/clippy/clippy_lints/src/methods/wrong_self_convention.rs b/src/tools/clippy/clippy_lints/src/methods/wrong_self_convention.rs
index 1fbf783b8..0a810a13f 100644
--- a/src/tools/clippy/clippy_lints/src/methods/wrong_self_convention.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/wrong_self_convention.rs
@@ -3,7 +3,7 @@ use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::ty::is_copy;
use rustc_lint::LateContext;
use rustc_middle::ty::Ty;
-use rustc_span::source_map::Span;
+use rustc_span::Span;
use std::fmt;
use super::WRONG_SELF_CONVENTION;
diff --git a/src/tools/clippy/clippy_lints/src/min_ident_chars.rs b/src/tools/clippy/clippy_lints/src/min_ident_chars.rs
index c79a1a7b9..4ad12e899 100644
--- a/src/tools/clippy/clippy_lints/src/min_ident_chars.rs
+++ b/src/tools/clippy/clippy_lints/src/min_ident_chars.rs
@@ -107,13 +107,17 @@ impl Visitor<'_> for IdentVisitor<'_, '_> {
let str = ident.as_str();
if conf.is_ident_too_short(cx, str, ident.span) {
- if let Node::Item(item) = node && let ItemKind::Use(..) = item.kind {
+ if let Node::Item(item) = node
+ && let ItemKind::Use(..) = item.kind
+ {
return;
}
// `struct Awa<T>(T)`
// ^
if let Node::PathSegment(path) = node {
- if let Res::Def(def_kind, ..) = path.res && let DefKind::TyParam = def_kind {
+ if let Res::Def(def_kind, ..) = path.res
+ && let DefKind::TyParam = def_kind
+ {
return;
}
if matches!(path.res, Res::PrimTy(..)) || path.res.opt_def_id().is_some_and(|def_id| !def_id.is_local())
diff --git a/src/tools/clippy/clippy_lints/src/misc.rs b/src/tools/clippy/clippy_lints/src/misc.rs
index 9c8b47fb3..f4af5f37b 100644
--- a/src/tools/clippy/clippy_lints/src/misc.rs
+++ b/src/tools/clippy/clippy_lints/src/misc.rs
@@ -16,7 +16,7 @@ use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::def_id::LocalDefId;
-use rustc_span::source_map::Span;
+use rustc_span::Span;
use crate::ref_patterns::REF_PATTERNS;
@@ -41,12 +41,12 @@ declare_clippy_lint! {
/// dereferences, e.g., changing `*x` to `x` within the function.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// fn foo(ref _x: u8) {}
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// fn foo(_x: &u8) {}
/// ```
#[clippy::version = "pre 1.29.0"]
@@ -70,7 +70,7 @@ declare_clippy_lint! {
/// macro, it has been allowed in the mean time.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let _x = 0;
/// let y = _x + 1; // Here we are using `_x`, even though it has a leading
/// // underscore. We should rename `_x` to `x`
@@ -263,7 +263,7 @@ impl<'tcx> LateLintPass<'tcx> for LintPass {
),
|diag| {
diag.span_note(definition_span, format!("`{name}` is defined here"));
- }
+ },
);
}
}
diff --git a/src/tools/clippy/clippy_lints/src/misc_early/mod.rs b/src/tools/clippy/clippy_lints/src/misc_early/mod.rs
index b226b8781..df0dd9e4e 100644
--- a/src/tools/clippy/clippy_lints/src/misc_early/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/misc_early/mod.rs
@@ -17,7 +17,7 @@ use rustc_data_structures::fx::FxHashMap;
use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::source_map::Span;
+use rustc_span::Span;
declare_clippy_lint! {
/// ### What it does
@@ -28,7 +28,7 @@ declare_clippy_lint! {
/// the fields that are actually bound.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # struct Foo {
/// # a: i32,
/// # b: i32,
@@ -43,7 +43,7 @@ declare_clippy_lint! {
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # struct Foo {
/// # a: i32,
/// # b: i32,
@@ -71,12 +71,12 @@ declare_clippy_lint! {
/// It affects code readability.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// fn foo(a: i32, _a: i32) {}
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// fn bar(a: i32, _b: i32) {}
/// ```
#[clippy::version = "pre 1.29.0"]
@@ -94,7 +94,7 @@ declare_clippy_lint! {
/// decremented.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let mut x = 3;
/// --x;
/// ```
@@ -113,14 +113,14 @@ declare_clippy_lint! {
/// It looks confusing.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # let _ =
/// 0x1a9BAcD
/// # ;
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # let _ =
/// 0x1A9BACD
/// # ;
@@ -142,14 +142,14 @@ declare_clippy_lint! {
/// Suffix style should be consistent.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # let _ =
/// 123832i32
/// # ;
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # let _ =
/// 123832_i32
/// # ;
@@ -170,14 +170,14 @@ declare_clippy_lint! {
/// Suffix style should be consistent.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # let _ =
/// 123832_i32
/// # ;
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # let _ =
/// 123832i32
/// # ;
@@ -202,7 +202,7 @@ declare_clippy_lint! {
/// ### Example
///
/// In Rust:
- /// ```rust
+ /// ```no_run
/// fn main() {
/// let a = 0123;
/// println!("{}", a);
@@ -258,7 +258,7 @@ declare_clippy_lint! {
/// bindings.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # let v = Some("abc");
/// match v {
/// Some(x) => (),
@@ -267,7 +267,7 @@ declare_clippy_lint! {
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # let v = Some("abc");
/// match v {
/// Some(x) => (),
@@ -295,7 +295,7 @@ declare_clippy_lint! {
/// can match that element as well.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # struct TupleStruct(u32, u32, u32);
/// # let t = TupleStruct(1, 2, 3);
/// match t {
@@ -305,7 +305,7 @@ declare_clippy_lint! {
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # struct TupleStruct(u32, u32, u32);
/// # let t = TupleStruct(1, 2, 3);
/// match t {
diff --git a/src/tools/clippy/clippy_lints/src/misc_early/unneeded_wildcard_pattern.rs b/src/tools/clippy/clippy_lints/src/misc_early/unneeded_wildcard_pattern.rs
index 7c4ae746e..00f46629f 100644
--- a/src/tools/clippy/clippy_lints/src/misc_early/unneeded_wildcard_pattern.rs
+++ b/src/tools/clippy/clippy_lints/src/misc_early/unneeded_wildcard_pattern.rs
@@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg;
use rustc_ast::ast::{Pat, PatKind};
use rustc_errors::Applicability;
use rustc_lint::EarlyContext;
-use rustc_span::source_map::Span;
+use rustc_span::Span;
use super::UNNEEDED_WILDCARD_PATTERN;
diff --git a/src/tools/clippy/clippy_lints/src/mismatching_type_param_order.rs b/src/tools/clippy/clippy_lints/src/mismatching_type_param_order.rs
index 28e041dee..0d79ece08 100644
--- a/src/tools/clippy/clippy_lints/src/mismatching_type_param_order.rs
+++ b/src/tools/clippy/clippy_lints/src/mismatching_type_param_order.rs
@@ -24,7 +24,7 @@ declare_clippy_lint! {
/// ignored.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// struct Foo<A, B> {
/// x: A,
/// y: B,
@@ -33,7 +33,7 @@ declare_clippy_lint! {
/// impl<B, A> Foo<B, A> {}
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// struct Foo<A, B> {
/// x: A,
/// y: B,
diff --git a/src/tools/clippy/clippy_lints/src/missing_assert_message.rs b/src/tools/clippy/clippy_lints/src/missing_assert_message.rs
index c17f00c42..4e00215c5 100644
--- a/src/tools/clippy/clippy_lints/src/missing_assert_message.rs
+++ b/src/tools/clippy/clippy_lints/src/missing_assert_message.rs
@@ -15,6 +15,10 @@ declare_clippy_lint! {
/// A good custom message should be more about why the failure of the assertion is problematic
/// and not what is failed because the assertion already conveys that.
///
+ /// Although the same reasoning applies to testing functions, this lint ignores them as they would be too noisy.
+ /// Also, in most cases understanding the test failure would be easier
+ /// compared to understanding a complex invariant distributed around the codebase.
+ ///
/// ### Known problems
/// This lint cannot check the quality of the custom panic messages.
/// Hence, you can suppress this lint simply by adding placeholder messages
@@ -23,14 +27,14 @@ declare_clippy_lint! {
/// don't provide any extra information.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # struct Service { ready: bool }
/// fn call(service: Service) {
/// assert!(service.ready);
/// }
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # struct Service { ready: bool }
/// fn call(service: Service) {
/// assert!(service.ready, "`service.poll_ready()` must be called first to ensure that service is ready to receive requests");
diff --git a/src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs b/src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs
index 08fec2b8e..dccf72d3c 100644
--- a/src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs
+++ b/src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs
@@ -43,14 +43,14 @@ declare_clippy_lint! {
/// about the length of a slice, but this lint will not detect that.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// fn sum(v: &[u8]) -> u8 {
/// // 4 bounds checks
/// v[0] + v[1] + v[2] + v[3]
/// }
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// fn sum(v: &[u8]) -> u8 {
/// assert!(v.len() > 4);
/// // no bounds checks
@@ -200,9 +200,13 @@ impl<'hir> IndexEntry<'hir> {
/// E.g. for `5` this returns `Some(5)`, for `..5` this returns `Some(4)`,
/// for `..=5` this returns `Some(5)`
fn upper_index_expr(expr: &Expr<'_>) -> Option<usize> {
- if let ExprKind::Lit(lit) = &expr.kind && let LitKind::Int(index, _) = lit.node {
+ if let ExprKind::Lit(lit) = &expr.kind
+ && let LitKind::Int(index, _) = lit.node
+ {
Some(index as usize)
- } else if let Some(higher::Range { end: Some(end), limits, .. }) = higher::Range::hir(expr)
+ } else if let Some(higher::Range {
+ end: Some(end), limits, ..
+ }) = higher::Range::hir(expr)
&& let ExprKind::Lit(lit) = &end.kind
&& let LitKind::Int(index @ 1.., _) = lit.node
{
@@ -228,7 +232,12 @@ fn check_index<'hir>(cx: &LateContext<'_>, expr: &'hir Expr<'hir>, map: &mut Unh
if let Some(entry) = entry {
match entry {
- IndexEntry::StrayAssert { asserted_len, comparison, assert_span, slice } => {
+ IndexEntry::StrayAssert {
+ asserted_len,
+ comparison,
+ assert_span,
+ slice,
+ } => {
*entry = IndexEntry::AssertWithIndex {
highest_index: index,
asserted_len: *asserted_len,
@@ -238,8 +247,12 @@ fn check_index<'hir>(cx: &LateContext<'_>, expr: &'hir Expr<'hir>, map: &mut Unh
comparison: *comparison,
};
},
- IndexEntry::IndexWithoutAssert { highest_index, indexes, .. }
- | IndexEntry::AssertWithIndex { highest_index, indexes, .. } => {
+ IndexEntry::IndexWithoutAssert {
+ highest_index, indexes, ..
+ }
+ | IndexEntry::AssertWithIndex {
+ highest_index, indexes, ..
+ } => {
indexes.push(expr.span);
*highest_index = (*highest_index).max(index);
},
diff --git a/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs b/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs
index f598a65d2..97522cbe6 100644
--- a/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs
+++ b/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs
@@ -1,5 +1,5 @@
+use clippy_config::msrvs::{self, Msrv};
use clippy_utils::diagnostics::span_lint;
-use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::qualify_min_const_fn::is_min_const_fn;
use clippy_utils::ty::has_drop;
use clippy_utils::{fn_has_unsatisfiable_preds, is_entrypoint_fn, is_from_proc_macro, trait_ref_of_method};
@@ -27,7 +27,7 @@ declare_clippy_lint! {
///
/// Also, the lint only runs one pass over the code. Consider these two non-const functions:
///
- /// ```rust
+ /// ```no_run
/// fn a() -> i32 {
/// 0
/// }
@@ -42,7 +42,7 @@ declare_clippy_lint! {
///
/// If you are marking a public function with `const`, removing it again will break API compatibility.
/// ### Example
- /// ```rust
+ /// ```no_run
/// # struct Foo {
/// # random_number: usize,
/// # }
@@ -55,7 +55,7 @@ declare_clippy_lint! {
///
/// Could be a const fn:
///
- /// ```rust
+ /// ```no_run
/// # struct Foo {
/// # random_number: usize,
/// # }
diff --git a/src/tools/clippy/clippy_lints/src/missing_doc.rs b/src/tools/clippy/clippy_lints/src/missing_doc.rs
index f2773cad4..973caa72b 100644
--- a/src/tools/clippy/clippy_lints/src/missing_doc.rs
+++ b/src/tools/clippy/clippy_lints/src/missing_doc.rs
@@ -16,8 +16,7 @@ use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::ty::Visibility;
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::def_id::CRATE_DEF_ID;
-use rustc_span::source_map::Span;
-use rustc_span::sym;
+use rustc_span::{sym, Span};
declare_clippy_lint! {
/// ### What it does
@@ -67,7 +66,7 @@ impl MissingDoc {
if_chain! {
if let Some(meta) = meta;
if let MetaItemKind::List(list) = meta.kind;
- if let Some(meta) = list.get(0);
+ if let Some(meta) = list.first();
if let Some(name) = meta.ident();
then {
name.name == sym::include
diff --git a/src/tools/clippy/clippy_lints/src/missing_enforced_import_rename.rs b/src/tools/clippy/clippy_lints/src/missing_enforced_import_rename.rs
index 96d83e114..16ff98a59 100644
--- a/src/tools/clippy/clippy_lints/src/missing_enforced_import_rename.rs
+++ b/src/tools/clippy/clippy_lints/src/missing_enforced_import_rename.rs
@@ -1,6 +1,6 @@
+use clippy_config::types::Rename;
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_opt;
-
use rustc_data_structures::fx::FxHashMap;
use rustc_errors::Applicability;
use rustc_hir::def::Res;
@@ -10,13 +10,14 @@ use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::Symbol;
-use crate::utils::conf::Rename;
-
declare_clippy_lint! {
/// ### What it does
/// Checks for imports that do not rename the item as specified
/// in the `enforce-import-renames` config option.
///
+ /// Note: Even though this lint is warn-by-default, it will only trigger if
+ /// import renames are defined in the clippy.toml file.
+ ///
/// ### Why is this bad?
/// Consistency is important, if a project has defined import
/// renames they should be followed. More practically, some item names are too
@@ -38,7 +39,7 @@ declare_clippy_lint! {
/// ```
#[clippy::version = "1.55.0"]
pub MISSING_ENFORCED_IMPORT_RENAMES,
- restriction,
+ style,
"enforce import renames"
}
diff --git a/src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs b/src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs
index 2f63b9b9f..95f9df4e4 100644
--- a/src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs
+++ b/src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs
@@ -1,9 +1,9 @@
use std::ops::ControlFlow;
use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::ty::match_type;
+use clippy_utils::is_path_lang_item;
+use clippy_utils::ty::is_type_diagnostic_item;
use clippy_utils::visitors::{for_each_expr, Visitable};
-use clippy_utils::{is_path_lang_item, paths};
use rustc_ast::LitKind;
use rustc_data_structures::fx::FxHashSet;
use rustc_hir::def::{DefKind, Res};
@@ -40,7 +40,7 @@ declare_clippy_lint! {
/// making it much less likely to accidentally forget to update the `Debug` impl when adding a new variant.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// use std::fmt;
/// struct Foo {
/// data: String,
@@ -57,7 +57,7 @@ declare_clippy_lint! {
/// }
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// use std::fmt;
/// struct Foo {
/// data: String,
@@ -114,9 +114,11 @@ fn should_lint<'tcx>(
if let ExprKind::MethodCall(path, recv, ..) = &expr.kind {
let recv_ty = typeck_results.expr_ty(recv).peel_refs();
- if path.ident.name == sym::debug_struct && match_type(cx, recv_ty, &paths::FORMATTER) {
+ if path.ident.name == sym::debug_struct && is_type_diagnostic_item(cx, recv_ty, sym::Formatter) {
has_debug_struct = true;
- } else if path.ident.name == sym!(finish_non_exhaustive) && match_type(cx, recv_ty, &paths::DEBUG_STRUCT) {
+ } else if path.ident.name == sym!(finish_non_exhaustive)
+ && is_type_diagnostic_item(cx, recv_ty, sym::DebugStruct)
+ {
has_finish_non_exhaustive = true;
}
}
@@ -137,7 +139,7 @@ fn as_field_call<'tcx>(
) -> Option<Symbol> {
if let ExprKind::MethodCall(path, recv, [debug_field, _], _) = &expr.kind
&& let recv_ty = typeck_results.expr_ty(recv).peel_refs()
- && match_type(cx, recv_ty, &paths::DEBUG_STRUCT)
+ && is_type_diagnostic_item(cx, recv_ty, sym::DebugStruct)
&& path.ident.name == sym::field
&& let ExprKind::Lit(lit) = &debug_field.kind
&& let LitKind::Str(sym, ..) = lit.node
diff --git a/src/tools/clippy/clippy_lints/src/missing_inline.rs b/src/tools/clippy/clippy_lints/src/missing_inline.rs
index 93f6025c7..b815da79b 100644
--- a/src/tools/clippy/clippy_lints/src/missing_inline.rs
+++ b/src/tools/clippy/clippy_lints/src/missing_inline.rs
@@ -3,8 +3,7 @@ use rustc_ast::ast;
use rustc_hir as hir;
use rustc_lint::{self, LateContext, LateLintPass, LintContext};
use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::source_map::Span;
-use rustc_span::sym;
+use rustc_span::{sym, Span};
declare_clippy_lint! {
/// ### What it does
@@ -21,7 +20,7 @@ declare_clippy_lint! {
/// then opt out for specific methods where this might not make sense.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// pub fn foo() {} // missing #[inline]
/// fn ok() {} // ok
/// #[inline] pub fn bar() {} // ok
diff --git a/src/tools/clippy/clippy_lints/src/missing_trait_methods.rs b/src/tools/clippy/clippy_lints/src/missing_trait_methods.rs
index 1adecd2ca..ad5f45a32 100644
--- a/src/tools/clippy/clippy_lints/src/missing_trait_methods.rs
+++ b/src/tools/clippy/clippy_lints/src/missing_trait_methods.rs
@@ -21,7 +21,7 @@ declare_clippy_lint! {
/// Indicates that a method is missing.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// trait Trait {
/// fn required();
///
@@ -35,7 +35,7 @@ declare_clippy_lint! {
/// }
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// trait Trait {
/// fn required();
///
diff --git a/src/tools/clippy/clippy_lints/src/mixed_read_write_in_expression.rs b/src/tools/clippy/clippy_lints/src/mixed_read_write_in_expression.rs
index 367cd6bd4..215161b04 100644
--- a/src/tools/clippy/clippy_lints/src/mixed_read_write_in_expression.rs
+++ b/src/tools/clippy/clippy_lints/src/mixed_read_write_in_expression.rs
@@ -22,7 +22,7 @@ declare_clippy_lint! {
/// order, or which is correct for any evaluation order.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let mut x = 0;
///
/// let a = {
@@ -33,7 +33,7 @@ declare_clippy_lint! {
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # let mut x = 0;
/// let tmp = {
/// x = 1;
@@ -134,15 +134,27 @@ impl<'a, 'tcx> DivergenceVisitor<'a, 'tcx> {
}
}
+fn stmt_might_diverge(stmt: &Stmt<'_>) -> bool {
+ !matches!(stmt.kind, StmtKind::Item(..))
+}
+
impl<'a, 'tcx> Visitor<'tcx> for DivergenceVisitor<'a, 'tcx> {
fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
match e.kind {
// fix #10776
ExprKind::Block(block, ..) => match (block.stmts, block.expr) {
- ([], Some(e)) => self.visit_expr(e),
- ([stmt], None) => match stmt.kind {
- StmtKind::Expr(e) | StmtKind::Semi(e) => self.visit_expr(e),
- _ => {},
+ (stmts, Some(e)) => {
+ if stmts.iter().all(|stmt| !stmt_might_diverge(stmt)) {
+ self.visit_expr(e);
+ }
+ },
+ ([first @ .., stmt], None) => {
+ if first.iter().all(|stmt| !stmt_might_diverge(stmt)) {
+ match stmt.kind {
+ StmtKind::Expr(e) | StmtKind::Semi(e) => self.visit_expr(e),
+ _ => {},
+ }
+ }
},
_ => {},
},
diff --git a/src/tools/clippy/clippy_lints/src/multi_assignments.rs b/src/tools/clippy/clippy_lints/src/multi_assignments.rs
index 81eb1a085..b42dce7a1 100644
--- a/src/tools/clippy/clippy_lints/src/multi_assignments.rs
+++ b/src/tools/clippy/clippy_lints/src/multi_assignments.rs
@@ -13,12 +13,12 @@ declare_clippy_lint! {
/// where such assignments return a copy of the assigned value.
///
/// ### Example
- /// ```rust
+ /// ```no_run
///# let (a, b);
/// a = b = 42;
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
///# let (a, b);
/// b = 42;
/// a = b;
diff --git a/src/tools/clippy/clippy_lints/src/multiple_unsafe_ops_per_block.rs b/src/tools/clippy/clippy_lints/src/multiple_unsafe_ops_per_block.rs
index fe35126aa..d4f8008ae 100644
--- a/src/tools/clippy/clippy_lints/src/multiple_unsafe_ops_per_block.rs
+++ b/src/tools/clippy/clippy_lints/src/multiple_unsafe_ops_per_block.rs
@@ -9,7 +9,7 @@ use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::lint::in_external_macro;
use rustc_middle::ty;
use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::Span;
+use rustc_span::{DesugaringKind, Span};
declare_clippy_lint! {
/// ### What it does
@@ -22,7 +22,7 @@ declare_clippy_lint! {
/// elimination of unnecessary unsafe blocks through refactoring.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// /// Reads a `char` from the given pointer.
/// ///
/// /// # Safety
@@ -36,7 +36,7 @@ declare_clippy_lint! {
/// }
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// /// Reads a `char` from the given pointer.
/// ///
/// /// # Safety
@@ -64,7 +64,10 @@ declare_lint_pass!(MultipleUnsafeOpsPerBlock => [MULTIPLE_UNSAFE_OPS_PER_BLOCK])
impl<'tcx> LateLintPass<'tcx> for MultipleUnsafeOpsPerBlock {
fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx hir::Block<'_>) {
- if !matches!(block.rules, BlockCheckMode::UnsafeBlock(_)) || in_external_macro(cx.tcx.sess, block.span) {
+ if !matches!(block.rules, BlockCheckMode::UnsafeBlock(_))
+ || in_external_macro(cx.tcx.sess, block.span)
+ || block.span.is_desugaring(DesugaringKind::Await)
+ {
return;
}
let mut unsafe_ops = vec![];
diff --git a/src/tools/clippy/clippy_lints/src/mut_key.rs b/src/tools/clippy/clippy_lints/src/mut_key.rs
index 5878f8995..ebfd53f1e 100644
--- a/src/tools/clippy/clippy_lints/src/mut_key.rs
+++ b/src/tools/clippy/clippy_lints/src/mut_key.rs
@@ -8,7 +8,7 @@ use rustc_middle::query::Key;
use rustc_middle::ty::{Adt, Ty};
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::def_id::LocalDefId;
-use rustc_span::source_map::Span;
+use rustc_span::Span;
use rustc_span::symbol::sym;
use std::iter;
@@ -47,7 +47,7 @@ declare_clippy_lint! {
/// [#6745](https://github.com/rust-lang/rust-clippy/issues/6745).
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// use std::cmp::{PartialEq, Eq};
/// use std::collections::HashSet;
/// use std::hash::{Hash, Hasher};
diff --git a/src/tools/clippy/clippy_lints/src/mut_mut.rs b/src/tools/clippy/clippy_lints/src/mut_mut.rs
index 64d8333a0..6989504a4 100644
--- a/src/tools/clippy/clippy_lints/src/mut_mut.rs
+++ b/src/tools/clippy/clippy_lints/src/mut_mut.rs
@@ -17,7 +17,7 @@ declare_clippy_lint! {
/// misunderstanding of references.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # let mut y = 1;
/// let x = &mut &mut y;
/// ```
diff --git a/src/tools/clippy/clippy_lints/src/mut_reference.rs b/src/tools/clippy/clippy_lints/src/mut_reference.rs
index e53e146ec..4f8e24422 100644
--- a/src/tools/clippy/clippy_lints/src/mut_reference.rs
+++ b/src/tools/clippy/clippy_lints/src/mut_reference.rs
@@ -15,14 +15,14 @@ declare_clippy_lint! {
/// the value. Also the code misleads about the intent of the call site.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # let mut vec = Vec::new();
/// # let mut value = 5;
/// vec.push(&mut value);
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # let mut vec = Vec::new();
/// # let value = 5;
/// vec.push(&value);
@@ -49,7 +49,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryMutPassed {
cx,
arguments.iter().collect(),
cx.typeck_results().expr_ty(fn_expr),
- &rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_qpath(path, false)),
+ &rustc_hir_pretty::qpath_to_string(path),
"function",
);
}
diff --git a/src/tools/clippy/clippy_lints/src/mutex_atomic.rs b/src/tools/clippy/clippy_lints/src/mutex_atomic.rs
index 99394b9e5..9d8c06cd0 100644
--- a/src/tools/clippy/clippy_lints/src/mutex_atomic.rs
+++ b/src/tools/clippy/clippy_lints/src/mutex_atomic.rs
@@ -29,14 +29,14 @@ declare_clippy_lint! {
/// for waiting before a critical section.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # let y = true;
/// # use std::sync::Mutex;
/// let x = Mutex::new(&y);
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # let y = true;
/// # use std::sync::atomic::AtomicBool;
/// let x = AtomicBool::new(y);
@@ -62,13 +62,13 @@ declare_clippy_lint! {
/// for waiting before a critical section.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # use std::sync::Mutex;
/// let x = Mutex::new(0usize);
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # use std::sync::atomic::AtomicUsize;
/// let x = AtomicUsize::new(0usize);
/// ```
diff --git a/src/tools/clippy/clippy_lints/src/needless_arbitrary_self_type.rs b/src/tools/clippy/clippy_lints/src/needless_arbitrary_self_type.rs
index 5457eeec4..97e8f1c03 100644
--- a/src/tools/clippy/clippy_lints/src/needless_arbitrary_self_type.rs
+++ b/src/tools/clippy/clippy_lints/src/needless_arbitrary_self_type.rs
@@ -15,7 +15,7 @@ declare_clippy_lint! {
/// Increases the amount and decreases the readability of code
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// enum ValType {
/// I32,
/// I64,
@@ -35,7 +35,7 @@ declare_clippy_lint! {
///
/// Could be rewritten as
///
- /// ```rust
+ /// ```no_run
/// enum ValType {
/// I32,
/// I64,
diff --git a/src/tools/clippy/clippy_lints/src/needless_bool.rs b/src/tools/clippy/clippy_lints/src/needless_bool.rs
index f6b87b071..02c177c92 100644
--- a/src/tools/clippy/clippy_lints/src/needless_bool.rs
+++ b/src/tools/clippy/clippy_lints/src/needless_bool.rs
@@ -32,7 +32,7 @@ declare_clippy_lint! {
/// shorter code.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # let x = true;
/// if x {
/// false
@@ -43,7 +43,7 @@ declare_clippy_lint! {
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # let x = true;
/// !x
/// # ;
@@ -207,9 +207,9 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBool {
_ => (),
}
}
- if let Some((lhs_a, a)) = fetch_assign(then) &&
- let Some((lhs_b, b)) = fetch_assign(r#else) &&
- SpanlessEq::new(cx).eq_expr(lhs_a, lhs_b)
+ if let Some((lhs_a, a)) = fetch_assign(then)
+ && let Some((lhs_b, b)) = fetch_assign(r#else)
+ && SpanlessEq::new(cx).eq_expr(lhs_a, lhs_b)
{
let mut applicability = Applicability::MachineApplicable;
let cond = Sugg::hir_with_applicability(cx, cond, "..", &mut applicability);
@@ -226,7 +226,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBool {
"this if-then-else expression assigns a bool literal",
"you can reduce it to",
sugg,
- applicability
+ applicability,
);
}
}
diff --git a/src/tools/clippy/clippy_lints/src/needless_borrowed_ref.rs b/src/tools/clippy/clippy_lints/src/needless_borrowed_ref.rs
index 11bf9e9ca..fdb91f0dc 100644
--- a/src/tools/clippy/clippy_lints/src/needless_borrowed_ref.rs
+++ b/src/tools/clippy/clippy_lints/src/needless_borrowed_ref.rs
@@ -13,7 +13,7 @@ declare_clippy_lint! {
/// This pattern has no effect in almost all cases.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let mut v = Vec::<String>::new();
/// v.iter_mut().filter(|&ref a| a.is_empty());
///
@@ -21,7 +21,7 @@ declare_clippy_lint! {
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// let mut v = Vec::<String>::new();
/// v.iter_mut().filter(|a| a.is_empty());
///
diff --git a/src/tools/clippy/clippy_lints/src/needless_borrows_for_generic_args.rs b/src/tools/clippy/clippy_lints/src/needless_borrows_for_generic_args.rs
index d55c77a92..dcfb109a4 100644
--- a/src/tools/clippy/clippy_lints/src/needless_borrows_for_generic_args.rs
+++ b/src/tools/clippy/clippy_lints/src/needless_borrows_for_generic_args.rs
@@ -1,6 +1,6 @@
+use clippy_config::msrvs::{self, Msrv};
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::mir::{enclosing_mir, expr_local, local_assignments, used_exactly_once, PossibleBorrowerMap};
-use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::source::snippet_with_context;
use clippy_utils::ty::is_copy;
use clippy_utils::{expr_use_ctxt, peel_n_hir_expr_refs, DefinedTy, ExprUseNode};
@@ -36,7 +36,7 @@ declare_clippy_lint! {
/// in such a case can change the semantics of the code.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// fn f(_: impl AsRef<str>) {}
///
/// let x = "foo";
@@ -44,7 +44,7 @@ declare_clippy_lint! {
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// fn f(_: impl AsRef<str>) {}
///
/// let x = "foo";
@@ -87,18 +87,24 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBorrowsForGenericArgs<'tcx> {
&& let ty::Param(ty) = *ty.value.skip_binder().kind()
&& let Some((hir_id, fn_id, i)) = match use_cx.node {
ExprUseNode::MethodArg(_, _, 0) => None,
- ExprUseNode::MethodArg(hir_id, None, i) => {
- cx.typeck_results().type_dependent_def_id(hir_id).map(|id| (hir_id, id, i))
- },
- ExprUseNode::FnArg(&Expr { kind: ExprKind::Path(ref p), hir_id, .. }, i)
- if !path_has_args(p) => match cx.typeck_results().qpath_res(p, hir_id) {
- Res::Def(DefKind::Fn | DefKind::Ctor(..) | DefKind::AssocFn, id) => {
- Some((hir_id, id, i))
+ ExprUseNode::MethodArg(hir_id, None, i) => cx
+ .typeck_results()
+ .type_dependent_def_id(hir_id)
+ .map(|id| (hir_id, id, i)),
+ ExprUseNode::FnArg(
+ &Expr {
+ kind: ExprKind::Path(ref p),
+ hir_id,
+ ..
},
+ i,
+ ) if !path_has_args(p) => match cx.typeck_results().qpath_res(p, hir_id) {
+ Res::Def(DefKind::Fn | DefKind::Ctor(..) | DefKind::AssocFn, id) => Some((hir_id, id, i)),
_ => None,
},
_ => None,
- } && let count = needless_borrow_count(
+ }
+ && let count = needless_borrow_count(
cx,
&mut self.possible_borrowers,
fn_id,
@@ -107,7 +113,8 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBorrowsForGenericArgs<'tcx> {
ty,
expr,
&self.msrv,
- ) && count != 0
+ )
+ && count != 0
{
span_lint_and_then(
cx,
@@ -119,7 +126,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBorrowsForGenericArgs<'tcx> {
let snip_span = peel_n_hir_expr_refs(expr, count).0.span;
let snip = snippet_with_context(cx, snip_span, expr.span.ctxt(), "..", &mut app).0;
diag.span_suggestion(expr.span, "change this to", snip.into_owned(), app);
- }
+ },
);
}
}
@@ -245,7 +252,9 @@ fn needless_borrow_count<'tcx>(
predicates.iter().all(|predicate| {
if let ClauseKind::Trait(trait_predicate) = predicate.kind().skip_binder()
- && cx.tcx.is_diagnostic_item(sym::IntoIterator, trait_predicate.trait_ref.def_id)
+ && cx
+ .tcx
+ .is_diagnostic_item(sym::IntoIterator, trait_predicate.trait_ref.def_id)
&& let ty::Param(param_ty) = trait_predicate.self_ty().kind()
&& let GenericArgKind::Type(ty) = args_with_referent_ty[param_ty.index as usize].unpack()
&& ty.is_array()
@@ -308,13 +317,13 @@ fn is_mixed_projection_predicate<'tcx>(
match projection_ty.self_ty().kind() {
ty::Alias(ty::Projection, inner_projection_ty) => {
projection_ty = *inner_projection_ty;
- }
+ },
ty::Param(param_ty) => {
return (param_ty.index as usize) >= generics.parent_count;
- }
+ },
_ => {
return false;
- }
+ },
}
}
} else {
diff --git a/src/tools/clippy/clippy_lints/src/needless_continue.rs b/src/tools/clippy/clippy_lints/src/needless_continue.rs
index 38a75034c..cb2738947 100644
--- a/src/tools/clippy/clippy_lints/src/needless_continue.rs
+++ b/src/tools/clippy/clippy_lints/src/needless_continue.rs
@@ -55,7 +55,7 @@ declare_clippy_lint! {
/// statement within the THEN block and omitting the else block completely.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # fn condition() -> bool { false }
/// # fn update_condition() {}
/// # let x = false;
@@ -72,7 +72,7 @@ declare_clippy_lint! {
///
/// Could be rewritten as
///
- /// ```rust
+ /// ```no_run
/// # fn condition() -> bool { false }
/// # fn update_condition() {}
/// # let x = false;
@@ -87,7 +87,7 @@ declare_clippy_lint! {
///
/// As another example, the following code
///
- /// ```rust
+ /// ```no_run
/// # fn waiting() -> bool { false }
/// loop {
/// if waiting() {
@@ -100,7 +100,7 @@ declare_clippy_lint! {
/// ```
/// Could be rewritten as
///
- /// ```rust
+ /// ```no_run
/// # fn waiting() -> bool { false }
/// loop {
/// if waiting() {
@@ -189,7 +189,7 @@ fn needless_continue_in_else(else_expr: &ast::Expr, label: Option<&ast::Label>)
}
fn is_first_block_stmt_continue(block: &ast::Block, label: Option<&ast::Label>) -> bool {
- block.stmts.get(0).map_or(false, |stmt| match stmt.kind {
+ block.stmts.first().map_or(false, |stmt| match stmt.kind {
ast::StmtKind::Semi(ref e) | ast::StmtKind::Expr(ref e) => {
if let ast::ExprKind::Continue(ref l) = e.kind {
compare_labels(label, l.as_ref())
@@ -408,7 +408,7 @@ fn check_and_warn(cx: &EarlyContext<'_>, expr: &ast::Expr) {
/// till a non-whitespace character is found. e.g., the string. If no closing `}` is present, the
/// string will be preserved.
///
-/// ```rust
+/// ```no_run
/// {
/// let x = 5;
/// }
@@ -434,7 +434,7 @@ fn erode_from_back(s: &str) -> String {
}
fn span_of_first_expr_in_block(block: &ast::Block) -> Option<Span> {
- block.stmts.get(0).map(|stmt| stmt.span)
+ block.stmts.first().map(|stmt| stmt.span)
}
#[cfg(test)]
diff --git a/src/tools/clippy/clippy_lints/src/needless_else.rs b/src/tools/clippy/clippy_lints/src/needless_else.rs
index 0c1fe881f..d881c13f8 100644
--- a/src/tools/clippy/clippy_lints/src/needless_else.rs
+++ b/src/tools/clippy/clippy_lints/src/needless_else.rs
@@ -13,7 +13,7 @@ declare_clippy_lint! {
/// An empty else branch does nothing and can be removed.
///
/// ### Example
- /// ```rust
+ /// ```no_run
///# fn check() -> bool { true }
/// if check() {
/// println!("Check successful!");
@@ -21,7 +21,7 @@ declare_clippy_lint! {
/// }
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
///# fn check() -> bool { true }
/// if check() {
/// println!("Check successful!");
diff --git a/src/tools/clippy/clippy_lints/src/needless_for_each.rs b/src/tools/clippy/clippy_lints/src/needless_for_each.rs
index 98bf122fa..c71996131 100644
--- a/src/tools/clippy/clippy_lints/src/needless_for_each.rs
+++ b/src/tools/clippy/clippy_lints/src/needless_for_each.rs
@@ -3,8 +3,7 @@ use rustc_hir::intravisit::{walk_expr, Visitor};
use rustc_hir::{Closure, Expr, ExprKind, Stmt, StmtKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::source_map::Span;
-use rustc_span::{sym, Symbol};
+use rustc_span::{sym, Span, Symbol};
use if_chain::if_chain;
@@ -25,14 +24,14 @@ declare_clippy_lint! {
/// But when none of these apply, a simple `for` loop is more idiomatic.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let v = vec![0, 1, 2];
/// v.iter().for_each(|elem| {
/// println!("{}", elem);
/// })
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// let v = vec![0, 1, 2];
/// for elem in v.iter() {
/// println!("{}", elem);
diff --git a/src/tools/clippy/clippy_lints/src/needless_if.rs b/src/tools/clippy/clippy_lints/src/needless_if.rs
index 1ed7ea6b3..23aabc548 100644
--- a/src/tools/clippy/clippy_lints/src/needless_if.rs
+++ b/src/tools/clippy/clippy_lints/src/needless_if.rs
@@ -44,7 +44,6 @@ impl LateLintPass<'_> for NeedlessIf {
&& block.stmts.is_empty()
&& block.expr.is_none()
&& !in_external_macro(cx.sess(), expr.span)
- && !is_from_proc_macro(cx, expr)
&& let Some(then_snippet) = snippet_opt(cx, then.span)
// Ignore
// - empty macro expansions
@@ -53,6 +52,7 @@ impl LateLintPass<'_> for NeedlessIf {
// - #[cfg]'d out code
&& then_snippet.chars().all(|ch| matches!(ch, '{' | '}') || ch.is_ascii_whitespace())
&& let Some(cond_snippet) = snippet_opt(cx, cond.span)
+ && !is_from_proc_macro(cx, expr)
{
span_lint_and_sugg(
cx,
diff --git a/src/tools/clippy/clippy_lints/src/needless_late_init.rs b/src/tools/clippy/clippy_lints/src/needless_late_init.rs
index 948454d13..c8888c744 100644
--- a/src/tools/clippy/clippy_lints/src/needless_late_init.rs
+++ b/src/tools/clippy/clippy_lints/src/needless_late_init.rs
@@ -22,7 +22,7 @@ declare_clippy_lint! {
/// Assigning in the `let` statement is less repetitive.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let a;
/// a = 1;
///
@@ -41,7 +41,7 @@ declare_clippy_lint! {
/// }
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// let a = 1;
///
/// let b = match 3 {
diff --git a/src/tools/clippy/clippy_lints/src/needless_parens_on_range_literals.rs b/src/tools/clippy/clippy_lints/src/needless_parens_on_range_literals.rs
index d17a383e8..7bbf1fb4c 100644
--- a/src/tools/clippy/clippy_lints/src/needless_parens_on_range_literals.rs
+++ b/src/tools/clippy/clippy_lints/src/needless_parens_on_range_literals.rs
@@ -20,7 +20,7 @@ declare_clippy_lint! {
///
/// ### Example
///
- /// ```rust
+ /// ```no_run
/// for i in (0)..10 {
/// println!("{i}");
/// }
@@ -28,7 +28,7 @@ declare_clippy_lint! {
///
/// Use instead:
///
- /// ```rust
+ /// ```no_run
/// for i in 0..10 {
/// println!("{i}");
/// }
@@ -46,9 +46,9 @@ fn snippet_enclosed_in_parenthesis(snippet: &str) -> bool {
}
fn check_for_parens(cx: &LateContext<'_>, e: &Expr<'_>, is_start: bool) {
- if is_start &&
- let ExprKind::Lit(literal) = e.kind &&
- let ast::LitKind::Float(_sym, ast::LitFloatType::Unsuffixed) = literal.node
+ if is_start
+ && let ExprKind::Lit(literal) = e.kind
+ && let ast::LitKind::Float(_sym, ast::LitFloatType::Unsuffixed) = literal.node
{
// don't check floating point literals on the start expression of a range
return;
diff --git a/src/tools/clippy/clippy_lints/src/needless_pass_by_ref_mut.rs b/src/tools/clippy/clippy_lints/src/needless_pass_by_ref_mut.rs
index 3ad9ae030..d610ba520 100644
--- a/src/tools/clippy/clippy_lints/src/needless_pass_by_ref_mut.rs
+++ b/src/tools/clippy/clippy_lints/src/needless_pass_by_ref_mut.rs
@@ -7,7 +7,8 @@ use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
use rustc_errors::Applicability;
use rustc_hir::intravisit::{walk_qpath, FnKind, Visitor};
use rustc_hir::{
- Body, Closure, Expr, ExprKind, FnDecl, HirId, HirIdMap, HirIdSet, Impl, ItemKind, Mutability, Node, PatKind, QPath,
+ BlockCheckMode, Body, Closure, Expr, ExprKind, FnDecl, HirId, HirIdMap, HirIdSet, Impl, ItemKind, Mutability, Node,
+ PatKind, QPath,
};
use rustc_hir_typeck::expr_use_visitor as euv;
use rustc_infer::infer::{InferCtxt, TyCtxtInferExt};
@@ -36,18 +37,18 @@ declare_clippy_lint! {
/// opportunities for parallelization.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// fn foo(y: &mut i32) -> i32 {
/// 12 + *y
/// }
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// fn foo(y: &i32) -> i32 {
/// 12 + *y
/// }
/// ```
- #[clippy::version = "1.72.0"]
+ #[clippy::version = "1.73.0"]
pub NEEDLESS_PASS_BY_REF_MUT,
nursery,
"using a `&mut` argument when it's not mutated"
@@ -139,13 +140,23 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut<'tcx> {
let hir_id = cx.tcx.hir().local_def_id_to_hir_id(fn_def_id);
let is_async = match kind {
FnKind::ItemFn(.., header) => {
+ if header.is_unsafe() {
+ // We don't check unsafe functions.
+ return;
+ }
let attrs = cx.tcx.hir().attrs(hir_id);
if header.abi != Abi::Rust || requires_exact_signature(attrs) {
return;
}
header.is_async()
},
- FnKind::Method(.., sig) => sig.header.is_async(),
+ FnKind::Method(.., sig) => {
+ if sig.header.is_unsafe() {
+ // We don't check unsafe functions.
+ return;
+ }
+ sig.header.is_async()
+ },
FnKind::Closure => return,
};
@@ -186,20 +197,21 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut<'tcx> {
};
let infcx = cx.tcx.infer_ctxt().build();
euv::ExprUseVisitor::new(&mut ctx, &infcx, fn_def_id, cx.param_env, cx.typeck_results()).consume_body(body);
- if is_async {
- let mut checked_closures = FxHashSet::default();
-
- // We retrieve all the closures declared in the async function because they will
- // not be found by `euv::Delegate`.
- let mut closures: FxHashSet<LocalDefId> = FxHashSet::default();
- for_each_expr_with_closures(cx, body, |expr| {
- if let ExprKind::Closure(closure) = expr.kind {
- closures.insert(closure.def_id);
- }
- ControlFlow::<()>::Continue(())
- });
- check_closures(&mut ctx, cx, &infcx, &mut checked_closures, closures);
+ let mut checked_closures = FxHashSet::default();
+
+ // We retrieve all the closures declared in the function because they will not be found
+ // by `euv::Delegate`.
+ let mut closures: FxHashSet<LocalDefId> = FxHashSet::default();
+ for_each_expr_with_closures(cx, body, |expr| {
+ if let ExprKind::Closure(closure) = expr.kind {
+ closures.insert(closure.def_id);
+ }
+ ControlFlow::<()>::Continue(())
+ });
+ check_closures(&mut ctx, cx, &infcx, &mut checked_closures, closures);
+
+ if is_async {
while !ctx.async_closures.is_empty() {
let async_closures = ctx.async_closures.clone();
ctx.async_closures.clear();
@@ -213,7 +225,10 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut<'tcx> {
if let PatKind::Binding(_, canonical_id, ..) = arg.pat.kind
&& !mutably_used_vars.contains(&canonical_id)
{
- self.fn_def_ids_to_maybe_unused_mut.entry(fn_def_id).or_default().push(input);
+ self.fn_def_ids_to_maybe_unused_mut
+ .entry(fn_def_id)
+ .or_default()
+ .push(input);
}
}
}
@@ -304,10 +319,27 @@ impl<'tcx> MutablyUsedVariablesCtxt<'tcx> {
}
self.aliases.insert(alias, target);
}
+
+ // The goal here is to find if the current scope is unsafe or not. It stops when it finds
+ // a function or an unsafe block.
+ fn is_in_unsafe_block(&self, item: HirId) -> bool {
+ let hir = self.tcx.hir();
+ for (parent, node) in hir.parent_iter(item) {
+ if let Some(fn_sig) = hir.fn_sig_by_hir_id(parent) {
+ return fn_sig.header.is_unsafe();
+ } else if let Node::Block(block) = node {
+ if matches!(block.rules, BlockCheckMode::UnsafeBlock(_)) {
+ return true;
+ }
+ }
+ }
+ false
+ }
}
impl<'tcx> euv::Delegate<'tcx> for MutablyUsedVariablesCtxt<'tcx> {
- fn consume(&mut self, cmt: &euv::PlaceWithHirId<'tcx>, _id: HirId) {
+ #[allow(clippy::if_same_then_else)]
+ fn consume(&mut self, cmt: &euv::PlaceWithHirId<'tcx>, id: HirId) {
if let euv::Place {
base:
euv::PlaceBase::Local(vid)
@@ -327,13 +359,18 @@ impl<'tcx> euv::Delegate<'tcx> for MutablyUsedVariablesCtxt<'tcx> {
&& matches!(base_ty.ref_mutability(), Some(Mutability::Mut))
{
self.add_mutably_used_var(*vid);
+ } else if self.is_in_unsafe_block(id) {
+ // If we are in an unsafe block, any operation on this variable must not be warned
+ // upon!
+ self.add_mutably_used_var(*vid);
}
self.prev_bind = None;
self.prev_move_to_closure.remove(vid);
}
}
- fn borrow(&mut self, cmt: &euv::PlaceWithHirId<'tcx>, _id: HirId, borrow: ty::BorrowKind) {
+ #[allow(clippy::if_same_then_else)]
+ fn borrow(&mut self, cmt: &euv::PlaceWithHirId<'tcx>, id: HirId, borrow: ty::BorrowKind) {
self.prev_bind = None;
if let euv::Place {
base:
@@ -355,6 +392,10 @@ impl<'tcx> euv::Delegate<'tcx> for MutablyUsedVariablesCtxt<'tcx> {
|| (borrow == ty::BorrowKind::UniqueImmBorrow && base_ty.ref_mutability() == Some(Mutability::Mut))
{
self.add_mutably_used_var(*vid);
+ } else if self.is_in_unsafe_block(id) {
+ // If we are in an unsafe block, any operation on this variable must not be warned
+ // upon!
+ self.add_mutably_used_var(*vid);
}
} else if borrow == ty::ImmBorrow {
// If there is an `async block`, it'll contain a call to a closure which we need to
@@ -397,7 +438,21 @@ impl<'tcx> euv::Delegate<'tcx> for MutablyUsedVariablesCtxt<'tcx> {
}
}
- fn copy(&mut self, _cmt: &euv::PlaceWithHirId<'tcx>, _id: HirId) {
+ fn copy(&mut self, cmt: &euv::PlaceWithHirId<'tcx>, id: HirId) {
+ if let euv::Place {
+ base:
+ euv::PlaceBase::Local(vid)
+ | euv::PlaceBase::Upvar(UpvarId {
+ var_path: UpvarPath { hir_id: vid },
+ ..
+ }),
+ ..
+ } = &cmt.place
+ {
+ if self.is_in_unsafe_block(id) {
+ self.add_mutably_used_var(*vid);
+ }
+ }
self.prev_bind = None;
}
@@ -427,8 +482,22 @@ impl<'tcx> euv::Delegate<'tcx> for MutablyUsedVariablesCtxt<'tcx> {
}
}
- fn bind(&mut self, _cmt: &euv::PlaceWithHirId<'tcx>, id: HirId) {
+ fn bind(&mut self, cmt: &euv::PlaceWithHirId<'tcx>, id: HirId) {
self.prev_bind = Some(id);
+ if let euv::Place {
+ base:
+ euv::PlaceBase::Local(vid)
+ | euv::PlaceBase::Upvar(UpvarId {
+ var_path: UpvarPath { hir_id: vid },
+ ..
+ }),
+ ..
+ } = &cmt.place
+ {
+ if self.is_in_unsafe_block(id) {
+ self.add_mutably_used_var(*vid);
+ }
+ }
}
}
@@ -454,7 +523,11 @@ impl<'tcx> Visitor<'tcx> for FnNeedsMutVisitor<'_, 'tcx> {
// #11182; do not lint if mutability is required elsewhere
if let Node::Expr(expr) = cx.tcx.hir().get(hir_id)
&& let Some(parent) = get_parent_node(cx.tcx, expr.hir_id)
- && let ty::FnDef(def_id, _) = cx.tcx.typeck(cx.tcx.hir().enclosing_body_owner(hir_id)).expr_ty(expr).kind()
+ && let ty::FnDef(def_id, _) = cx
+ .tcx
+ .typeck(cx.tcx.hir().enclosing_body_owner(hir_id))
+ .expr_ty(expr)
+ .kind()
&& let Some(def_id) = def_id.as_local()
{
if let Node::Expr(e) = parent
diff --git a/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs b/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs
index 5ee26966f..8fa461ac1 100644
--- a/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs
+++ b/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs
@@ -1,10 +1,10 @@
use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then};
+use clippy_utils::is_self;
use clippy_utils::ptr::get_spans;
use clippy_utils::source::{snippet, snippet_opt};
use clippy_utils::ty::{
implements_trait, implements_trait_with_env_from_iter, is_copy, is_type_diagnostic_item, is_type_lang_item,
};
-use clippy_utils::{get_trait_def_id, is_self, paths};
use if_chain::if_chain;
use rustc_ast::ast::Attribute;
use rustc_errors::{Applicability, Diagnostic};
@@ -43,13 +43,13 @@ declare_clippy_lint! {
/// (by using `Borrow` trait, for example), depending on how the function is used.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// fn foo(v: Vec<i32>) {
/// assert_eq!(v.len(), 42);
/// }
/// ```
/// should be
- /// ```rust
+ /// ```no_run
/// fn foo(v: &[i32]) {
/// assert_eq!(v.len(), 42);
/// }
@@ -115,7 +115,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue {
need!(cx.tcx.lang_items().fn_trait()),
need!(cx.tcx.lang_items().fn_once_trait()),
need!(cx.tcx.lang_items().fn_mut_trait()),
- need!(get_trait_def_id(cx, &paths::RANGE_ARGUMENT_TRAIT)),
+ need!(cx.tcx.get_diagnostic_item(sym::RangeBounds)),
];
let sized_trait = need!(cx.tcx.lang_items().sized_trait());
diff --git a/src/tools/clippy/clippy_lints/src/needless_question_mark.rs b/src/tools/clippy/clippy_lints/src/needless_question_mark.rs
index 7b0f7eaf1..074c9fef1 100644
--- a/src/tools/clippy/clippy_lints/src/needless_question_mark.rs
+++ b/src/tools/clippy/clippy_lints/src/needless_question_mark.rs
@@ -4,7 +4,7 @@ use clippy_utils::source::snippet;
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::def::{DefKind, Res};
-use rustc_hir::{AsyncGeneratorKind, Block, Body, Expr, ExprKind, GeneratorKind, LangItem, MatchSource, QPath};
+use rustc_hir::{Block, Body, CoroutineKind, CoroutineSource, Expr, ExprKind, LangItem, MatchSource, QPath};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
@@ -16,7 +16,7 @@ declare_clippy_lint! {
/// There's no reason to use `?` to short-circuit when execution of the body will end there anyway.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// struct TO {
/// magic: Option<usize>,
/// }
@@ -35,7 +35,7 @@ declare_clippy_lint! {
///
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// struct TO {
/// magic: Option<usize>,
/// }
@@ -87,7 +87,7 @@ impl LateLintPass<'_> for NeedlessQuestionMark {
}
fn check_body(&mut self, cx: &LateContext<'_>, body: &'_ Body<'_>) {
- if let Some(GeneratorKind::Async(AsyncGeneratorKind::Fn)) = body.generator_kind {
+ if let Some(CoroutineKind::Async(CoroutineSource::Fn)) = body.coroutine_kind {
if let ExprKind::Block(
Block {
expr:
@@ -125,7 +125,7 @@ fn check(cx: &LateContext<'_>, expr: &Expr<'_>) {
if let ExprKind::Match(inner_expr_with_q, _, MatchSource::TryDesugar(_)) = &arg.kind;
if let ExprKind::Call(called, [inner_expr]) = &inner_expr_with_q.kind;
if let ExprKind::Path(QPath::LangItem(LangItem::TryTraitBranch, ..)) = &called.kind;
- if expr.span.ctxt() == inner_expr.span.ctxt();
+ if expr.span.eq_ctxt(inner_expr.span);
let expr_ty = cx.typeck_results().expr_ty(expr);
let inner_ty = cx.typeck_results().expr_ty(inner_expr);
if expr_ty == inner_ty;
diff --git a/src/tools/clippy/clippy_lints/src/needless_update.rs b/src/tools/clippy/clippy_lints/src/needless_update.rs
index 0bd29d177..f8888d368 100644
--- a/src/tools/clippy/clippy_lints/src/needless_update.rs
+++ b/src/tools/clippy/clippy_lints/src/needless_update.rs
@@ -17,7 +17,7 @@ declare_clippy_lint! {
/// somewhere), and make the code less readable.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # struct Point {
/// # x: i32,
/// # y: i32,
diff --git a/src/tools/clippy/clippy_lints/src/neg_cmp_op_on_partial_ord.rs b/src/tools/clippy/clippy_lints/src/neg_cmp_op_on_partial_ord.rs
index a022fc156..56c67406d 100644
--- a/src/tools/clippy/clippy_lints/src/neg_cmp_op_on_partial_ord.rs
+++ b/src/tools/clippy/clippy_lints/src/neg_cmp_op_on_partial_ord.rs
@@ -18,7 +18,7 @@ declare_clippy_lint! {
/// especially easy to miss if the operator based comparison result is negated.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let a = 1.0;
/// let b = f64::NAN;
///
@@ -26,7 +26,7 @@ declare_clippy_lint! {
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// use std::cmp::Ordering;
/// # let a = 1.0;
/// # let b = f64::NAN;
diff --git a/src/tools/clippy/clippy_lints/src/neg_multiply.rs b/src/tools/clippy/clippy_lints/src/neg_multiply.rs
index db0e22842..8b69f94cb 100644
--- a/src/tools/clippy/clippy_lints/src/neg_multiply.rs
+++ b/src/tools/clippy/clippy_lints/src/neg_multiply.rs
@@ -8,7 +8,7 @@ use rustc_errors::Applicability;
use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::source_map::Span;
+use rustc_span::Span;
declare_clippy_lint! {
/// ### What it does
diff --git a/src/tools/clippy/clippy_lints/src/no_effect.rs b/src/tools/clippy/clippy_lints/src/no_effect.rs
index aee184252..3a28e511f 100644
--- a/src/tools/clippy/clippy_lints/src/no_effect.rs
+++ b/src/tools/clippy/clippy_lints/src/no_effect.rs
@@ -23,7 +23,7 @@ declare_clippy_lint! {
/// readable.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// 0;
/// ```
#[clippy::version = "pre 1.29.0"]
@@ -103,11 +103,16 @@ fn check_no_effect(cx: &LateContext<'_>, stmt: &Stmt<'_>) -> bool {
&& final_stmt.hir_id == stmt.hir_id
{
let expr_ty = cx.typeck_results().expr_ty(expr);
- let mut ret_ty = cx.tcx.fn_sig(item.owner_id).instantiate_identity().output().skip_binder();
+ let mut ret_ty = cx
+ .tcx
+ .fn_sig(item.owner_id)
+ .instantiate_identity()
+ .output()
+ .skip_binder();
// Remove `impl Future<Output = T>` to get `T`
- if cx.tcx.ty_is_opaque_future(ret_ty) &&
- let Some(true_ret_ty) = cx.tcx.infer_ctxt().build().get_impl_future_output_ty(ret_ty)
+ if cx.tcx.ty_is_opaque_future(ret_ty)
+ && let Some(true_ret_ty) = cx.tcx.infer_ctxt().build().get_impl_future_output_ty(ret_ty)
{
ret_ty = true_ret_ty;
}
diff --git a/src/tools/clippy/clippy_lints/src/no_mangle_with_rust_abi.rs b/src/tools/clippy/clippy_lints/src/no_mangle_with_rust_abi.rs
index 8fd9ae351..04d750148 100644
--- a/src/tools/clippy/clippy_lints/src/no_mangle_with_rust_abi.rs
+++ b/src/tools/clippy/clippy_lints/src/no_mangle_with_rust_abi.rs
@@ -18,13 +18,13 @@ declare_clippy_lint! {
/// Rust ABI can break this at any point.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// #[no_mangle]
/// fn example(arg_one: u32, arg_two: usize) {}
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// #[no_mangle]
/// extern "C" fn example(arg_one: u32, arg_two: usize) {}
/// ```
@@ -48,7 +48,8 @@ impl<'tcx> LateLintPass<'tcx> for NoMangleWithRustAbi {
&& let Some((fn_attrs, _)) = snippet.split_once("fn")
&& !fn_attrs.contains("extern")
{
- let sugg_span = fn_sig.span
+ let sugg_span = fn_sig
+ .span
.with_lo(fn_sig.span.lo() + BytePos::from_usize(fn_attrs.len()))
.shrink_to_lo();
diff --git a/src/tools/clippy/clippy_lints/src/non_canonical_impls.rs b/src/tools/clippy/clippy_lints/src/non_canonical_impls.rs
index 20b4b4f03..9689f63a0 100644
--- a/src/tools/clippy/clippy_lints/src/non_canonical_impls.rs
+++ b/src/tools/clippy/clippy_lints/src/non_canonical_impls.rs
@@ -1,7 +1,6 @@
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
-use clippy_utils::paths::ORD_CMP;
use clippy_utils::ty::implements_trait;
-use clippy_utils::{get_parent_node, is_res_lang_ctor, last_path_segment, match_def_path, path_res, std_or_core};
+use clippy_utils::{get_parent_node, is_res_lang_ctor, last_path_segment, path_res, std_or_core};
use rustc_errors::Applicability;
use rustc_hir::def_id::LocalDefId;
use rustc_hir::{Expr, ExprKind, ImplItem, ImplItemKind, LangItem, Node, UnOp};
@@ -65,7 +64,7 @@ declare_clippy_lint! {
/// in `Some`.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # use std::cmp::Ordering;
/// #[derive(Eq, PartialEq)]
/// struct A(u32);
@@ -85,7 +84,7 @@ declare_clippy_lint! {
/// }
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # use std::cmp::Ordering;
/// #[derive(Eq, PartialEq)]
/// struct A(u32);
@@ -103,7 +102,7 @@ declare_clippy_lint! {
/// }
/// }
/// ```
- #[clippy::version = "1.72.0"]
+ #[clippy::version = "1.73.0"]
pub NON_CANONICAL_PARTIAL_ORD_IMPL,
suspicious,
"non-canonical implementation of `PartialOrd` on an `Ord` type"
@@ -132,12 +131,7 @@ impl LateLintPass<'_> for NonCanonicalImpls {
if cx.tcx.is_diagnostic_item(sym::Clone, trait_impl.def_id)
&& let Some(copy_def_id) = cx.tcx.get_diagnostic_item(sym::Copy)
- && implements_trait(
- cx,
- trait_impl.self_ty(),
- copy_def_id,
- &[],
- )
+ && implements_trait(cx, trait_impl.self_ty(), copy_def_id, &[])
{
if impl_item.ident.name == sym::clone {
if block.stmts.is_empty()
@@ -145,7 +139,8 @@ impl LateLintPass<'_> for NonCanonicalImpls {
&& let ExprKind::Unary(UnOp::Deref, deref) = expr.kind
&& let ExprKind::Path(qpath) = deref.kind
&& last_path_segment(&qpath).ident.name == kw::SelfLower
- {} else {
+ {
+ } else {
span_lint_and_sugg(
cx,
NON_CANONICAL_CLONE_IMPL,
@@ -198,10 +193,13 @@ impl LateLintPass<'_> for NonCanonicalImpls {
&& is_res_lang_ctor(cx, cx.qpath_res(some_path, *some_hir_id), LangItem::OptionSome)
// Fix #11178, allow `Self::cmp(self, ..)` too
&& self_cmp_call(cx, cmp_expr, impl_item.owner_id.def_id, &mut needs_fully_qualified)
- {} else {
+ {
+ } else {
// If `Self` and `Rhs` are not the same type, bail. This makes creating a valid
// suggestion tons more complex.
- if let [lhs, rhs, ..] = trait_impl.args.as_slice() && lhs != rhs {
+ if let [lhs, rhs, ..] = trait_impl.args.as_slice()
+ && lhs != rhs
+ {
return;
}
@@ -239,12 +237,8 @@ impl LateLintPass<'_> for NonCanonicalImpls {
],
};
- diag.multipart_suggestion(
- "change this to",
- suggs,
- Applicability::Unspecified,
- );
- }
+ diag.multipart_suggestion("change this to", suggs, Applicability::Unspecified);
+ },
);
}
}
@@ -261,7 +255,7 @@ fn self_cmp_call<'tcx>(
match cmp_expr.kind {
ExprKind::Call(path, [_self, _other]) => path_res(cx, path)
.opt_def_id()
- .is_some_and(|def_id| match_def_path(cx, def_id, &ORD_CMP)),
+ .is_some_and(|def_id| cx.tcx.is_diagnostic_item(sym::ord_cmp_method, def_id)),
ExprKind::MethodCall(_, _, [_other], ..) => {
// We can set this to true here no matter what as if it's a `MethodCall` and goes to the
// `else` branch, it must be a method named `cmp` that isn't `Ord::cmp`
@@ -273,7 +267,7 @@ fn self_cmp_call<'tcx>(
cx.tcx
.typeck(def_id)
.type_dependent_def_id(cmp_expr.hir_id)
- .is_some_and(|def_id| match_def_path(cx, def_id, &ORD_CMP))
+ .is_some_and(|def_id| cx.tcx.is_diagnostic_item(sym::ord_cmp_method, def_id))
},
_ => false,
}
diff --git a/src/tools/clippy/clippy_lints/src/non_copy_const.rs b/src/tools/clippy/clippy_lints/src/non_copy_const.rs
index 2b4e3260c..54cec066b 100644
--- a/src/tools/clippy/clippy_lints/src/non_copy_const.rs
+++ b/src/tools/clippy/clippy_lints/src/non_copy_const.rs
@@ -5,9 +5,10 @@
use std::ptr;
use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::in_constant;
use clippy_utils::macros::macro_backtrace;
+use clippy_utils::{def_path_def_ids, in_constant};
use if_chain::if_chain;
+use rustc_data_structures::fx::FxHashSet;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::DefId;
use rustc_hir::{
@@ -15,9 +16,10 @@ use rustc_hir::{
};
use rustc_lint::{LateContext, LateLintPass, Lint};
use rustc_middle::mir::interpret::{ErrorHandled, EvalToValTreeResult, GlobalId};
+use rustc_middle::query::Key;
use rustc_middle::ty::adjustment::Adjust;
use rustc_middle::ty::{self, Ty, TyCtxt};
-use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::{sym, InnerSpan, Span};
use rustc_target::abi::VariantIdx;
@@ -56,7 +58,7 @@ declare_clippy_lint! {
/// [#5812](https://github.com/rust-lang/rust-clippy/issues/5812) and
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};
///
/// const CONST_ATOM: AtomicUsize = AtomicUsize::new(12);
@@ -65,7 +67,7 @@ declare_clippy_lint! {
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};
/// static STATIC_ATOM: AtomicUsize = AtomicUsize::new(15);
/// STATIC_ATOM.store(9, SeqCst);
@@ -103,7 +105,7 @@ declare_clippy_lint! {
/// [#3825](https://github.com/rust-lang/rust-clippy/issues/3825)
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};
/// const CONST_ATOM: AtomicUsize = AtomicUsize::new(12);
///
@@ -112,7 +114,7 @@ declare_clippy_lint! {
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};
/// const CONST_ATOM: AtomicUsize = AtomicUsize::new(12);
///
@@ -126,128 +128,6 @@ declare_clippy_lint! {
"referencing `const` with interior mutability"
}
-fn is_unfrozen<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
- // Ignore types whose layout is unknown since `is_freeze` reports every generic types as `!Freeze`,
- // making it indistinguishable from `UnsafeCell`. i.e. it isn't a tool to prove a type is
- // 'unfrozen'. However, this code causes a false negative in which
- // a type contains a layout-unknown type, but also an unsafe cell like `const CELL: Cell<T>`.
- // Yet, it's better than `ty.has_type_flags(TypeFlags::HAS_TY_PARAM | TypeFlags::HAS_PROJECTION)`
- // since it works when a pointer indirection involves (`Cell<*const T>`).
- // Making up a `ParamEnv` where every generic params and assoc types are `Freeze`is another option;
- // but I'm not sure whether it's a decent way, if possible.
- cx.tcx.layout_of(cx.param_env.and(ty)).is_ok() && !ty.is_freeze(cx.tcx, cx.param_env)
-}
-
-fn is_value_unfrozen_raw<'tcx>(
- cx: &LateContext<'tcx>,
- result: Result<Option<ty::ValTree<'tcx>>, ErrorHandled>,
- ty: Ty<'tcx>,
-) -> bool {
- fn inner<'tcx>(cx: &LateContext<'tcx>, val: ty::ValTree<'tcx>, ty: Ty<'tcx>) -> bool {
- match *ty.kind() {
- // the fact that we have to dig into every structs to search enums
- // leads us to the point checking `UnsafeCell` directly is the only option.
- ty::Adt(ty_def, ..) if ty_def.is_unsafe_cell() => true,
- // As of 2022-09-08 miri doesn't track which union field is active so there's no safe way to check the
- // contained value.
- ty::Adt(def, ..) if def.is_union() => false,
- ty::Array(ty, _) => val.unwrap_branch().iter().any(|field| inner(cx, *field, ty)),
- ty::Adt(def, _) if def.is_union() => false,
- ty::Adt(def, args) if def.is_enum() => {
- let (&variant_index, fields) = val.unwrap_branch().split_first().unwrap();
- let variant_index = VariantIdx::from_u32(variant_index.unwrap_leaf().try_to_u32().ok().unwrap());
- fields
- .iter()
- .copied()
- .zip(
- def.variants()[variant_index]
- .fields
- .iter()
- .map(|field| field.ty(cx.tcx, args)),
- )
- .any(|(field, ty)| inner(cx, field, ty))
- },
- ty::Adt(def, args) => val
- .unwrap_branch()
- .iter()
- .zip(def.non_enum_variant().fields.iter().map(|field| field.ty(cx.tcx, args)))
- .any(|(field, ty)| inner(cx, *field, ty)),
- ty::Tuple(tys) => val
- .unwrap_branch()
- .iter()
- .zip(tys)
- .any(|(field, ty)| inner(cx, *field, ty)),
- _ => false,
- }
- }
- result.map_or_else(
- |err| {
- // Consider `TooGeneric` cases as being unfrozen.
- // This causes a false positive where an assoc const whose type is unfrozen
- // have a value that is a frozen variant with a generic param (an example is
- // `declare_interior_mutable_const::enums::BothOfCellAndGeneric::GENERIC_VARIANT`).
- // However, it prevents a number of false negatives that is, I think, important:
- // 1. assoc consts in trait defs referring to consts of themselves (an example is
- // `declare_interior_mutable_const::traits::ConcreteTypes::ANOTHER_ATOMIC`).
- // 2. a path expr referring to assoc consts whose type is doesn't have any frozen variants in trait
- // defs (i.e. without substitute for `Self`). (e.g. borrowing
- // `borrow_interior_mutable_const::trait::ConcreteTypes::ATOMIC`)
- // 3. similar to the false positive above; but the value is an unfrozen variant, or the type has no
- // enums. (An example is
- // `declare_interior_mutable_const::enums::BothOfCellAndGeneric::UNFROZEN_VARIANT` and
- // `declare_interior_mutable_const::enums::BothOfCellAndGeneric::NO_ENUM`).
- // One might be able to prevent these FNs correctly, and replace this with `false`;
- // e.g. implementing `has_frozen_variant` described above, and not running this function
- // when the type doesn't have any frozen variants would be the 'correct' way for the 2nd
- // case (that actually removes another suboptimal behavior (I won't say 'false positive') where,
- // similar to 2., but with the a frozen variant) (e.g. borrowing
- // `borrow_interior_mutable_const::enums::AssocConsts::TO_BE_FROZEN_VARIANT`).
- // I chose this way because unfrozen enums as assoc consts are rare (or, hopefully, none).
- matches!(err, ErrorHandled::TooGeneric(..))
- },
- |val| val.map_or(true, |val| inner(cx, val, ty)),
- )
-}
-
-fn is_value_unfrozen_poly<'tcx>(cx: &LateContext<'tcx>, body_id: BodyId, ty: Ty<'tcx>) -> bool {
- let def_id = body_id.hir_id.owner.to_def_id();
- let args = ty::GenericArgs::identity_for_item(cx.tcx, def_id);
- let instance = ty::Instance::new(def_id, args);
- let cid = rustc_middle::mir::interpret::GlobalId {
- instance,
- promoted: None,
- };
- let param_env = cx.tcx.param_env(def_id).with_reveal_all_normalized(cx.tcx);
- let result = cx.tcx.const_eval_global_id_for_typeck(param_env, cid, None);
- is_value_unfrozen_raw(cx, result, ty)
-}
-
-fn is_value_unfrozen_expr<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId, def_id: DefId, ty: Ty<'tcx>) -> bool {
- let args = cx.typeck_results().node_args(hir_id);
-
- let result = const_eval_resolve(cx.tcx, cx.param_env, ty::UnevaluatedConst::new(def_id, args), None);
- is_value_unfrozen_raw(cx, result, ty)
-}
-
-pub fn const_eval_resolve<'tcx>(
- tcx: TyCtxt<'tcx>,
- param_env: ty::ParamEnv<'tcx>,
- ct: ty::UnevaluatedConst<'tcx>,
- span: Option<Span>,
-) -> EvalToValTreeResult<'tcx> {
- match ty::Instance::resolve(tcx, param_env, ct.def, ct.args) {
- Ok(Some(instance)) => {
- let cid = GlobalId {
- instance,
- promoted: None,
- };
- tcx.const_eval_global_id_for_typeck(param_env, cid, span)
- },
- Ok(None) => Err(ErrorHandled::TooGeneric(span.unwrap_or(rustc_span::DUMMY_SP))),
- Err(err) => Err(ErrorHandled::Reported(err.into(), span.unwrap_or(rustc_span::DUMMY_SP))),
- }
-}
-
#[derive(Copy, Clone)]
enum Source {
Item { item: Span },
@@ -292,13 +172,178 @@ fn lint(cx: &LateContext<'_>, source: Source) {
});
}
-declare_lint_pass!(NonCopyConst => [DECLARE_INTERIOR_MUTABLE_CONST, BORROW_INTERIOR_MUTABLE_CONST]);
+#[derive(Clone)]
+pub struct NonCopyConst {
+ ignore_interior_mutability: Vec<String>,
+ ignore_mut_def_ids: FxHashSet<DefId>,
+}
+
+impl_lint_pass!(NonCopyConst => [DECLARE_INTERIOR_MUTABLE_CONST, BORROW_INTERIOR_MUTABLE_CONST]);
+
+impl NonCopyConst {
+ pub fn new(ignore_interior_mutability: Vec<String>) -> Self {
+ Self {
+ ignore_interior_mutability,
+ ignore_mut_def_ids: FxHashSet::default(),
+ }
+ }
+
+ fn is_ty_ignored(&self, ty: Ty<'_>) -> bool {
+ matches!(ty.ty_adt_id(), Some(adt_id) if self.ignore_mut_def_ids.contains(&adt_id))
+ }
+
+ fn is_unfrozen<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
+ // Ignore types whose layout is unknown since `is_freeze` reports every generic types as `!Freeze`,
+ // making it indistinguishable from `UnsafeCell`. i.e. it isn't a tool to prove a type is
+ // 'unfrozen'. However, this code causes a false negative in which
+ // a type contains a layout-unknown type, but also an unsafe cell like `const CELL: Cell<T>`.
+ // Yet, it's better than `ty.has_type_flags(TypeFlags::HAS_TY_PARAM | TypeFlags::HAS_PROJECTION)`
+ // since it works when a pointer indirection involves (`Cell<*const T>`).
+ // Making up a `ParamEnv` where every generic params and assoc types are `Freeze`is another option;
+ // but I'm not sure whether it's a decent way, if possible.
+ cx.tcx.layout_of(cx.param_env.and(ty)).is_ok() && !ty.is_freeze(cx.tcx, cx.param_env)
+ }
+
+ fn is_value_unfrozen_raw_inner<'tcx>(&self, cx: &LateContext<'tcx>, val: ty::ValTree<'tcx>, ty: Ty<'tcx>) -> bool {
+ if self.is_ty_ignored(ty) {
+ return false;
+ }
+ match *ty.kind() {
+ // the fact that we have to dig into every structs to search enums
+ // leads us to the point checking `UnsafeCell` directly is the only option.
+ ty::Adt(ty_def, ..) if ty_def.is_unsafe_cell() => true,
+ // As of 2022-09-08 miri doesn't track which union field is active so there's no safe way to check the
+ // contained value.
+ ty::Adt(def, ..) if def.is_union() => false,
+ ty::Array(ty, _) => val
+ .unwrap_branch()
+ .iter()
+ .any(|field| self.is_value_unfrozen_raw_inner(cx, *field, ty)),
+ ty::Adt(def, _) if def.is_union() => false,
+ ty::Adt(def, args) if def.is_enum() => {
+ let (&variant_index, fields) = val.unwrap_branch().split_first().unwrap();
+ let variant_index = VariantIdx::from_u32(variant_index.unwrap_leaf().try_to_u32().ok().unwrap());
+ fields
+ .iter()
+ .copied()
+ .zip(
+ def.variants()[variant_index]
+ .fields
+ .iter()
+ .map(|field| field.ty(cx.tcx, args)),
+ )
+ .any(|(field, ty)| self.is_value_unfrozen_raw_inner(cx, field, ty))
+ },
+ ty::Adt(def, args) => val
+ .unwrap_branch()
+ .iter()
+ .zip(def.non_enum_variant().fields.iter().map(|field| field.ty(cx.tcx, args)))
+ .any(|(field, ty)| self.is_value_unfrozen_raw_inner(cx, *field, ty)),
+ ty::Tuple(tys) => val
+ .unwrap_branch()
+ .iter()
+ .zip(tys)
+ .any(|(field, ty)| self.is_value_unfrozen_raw_inner(cx, *field, ty)),
+ _ => false,
+ }
+ }
+
+ fn is_value_unfrozen_raw<'tcx>(
+ &self,
+ cx: &LateContext<'tcx>,
+ result: Result<Option<ty::ValTree<'tcx>>, ErrorHandled>,
+ ty: Ty<'tcx>,
+ ) -> bool {
+ result.map_or_else(
+ |err| {
+ // Consider `TooGeneric` cases as being unfrozen.
+ // This causes a false positive where an assoc const whose type is unfrozen
+ // have a value that is a frozen variant with a generic param (an example is
+ // `declare_interior_mutable_const::enums::BothOfCellAndGeneric::GENERIC_VARIANT`).
+ // However, it prevents a number of false negatives that is, I think, important:
+ // 1. assoc consts in trait defs referring to consts of themselves (an example is
+ // `declare_interior_mutable_const::traits::ConcreteTypes::ANOTHER_ATOMIC`).
+ // 2. a path expr referring to assoc consts whose type is doesn't have any frozen variants in trait
+ // defs (i.e. without substitute for `Self`). (e.g. borrowing
+ // `borrow_interior_mutable_const::trait::ConcreteTypes::ATOMIC`)
+ // 3. similar to the false positive above; but the value is an unfrozen variant, or the type has no
+ // enums. (An example is
+ // `declare_interior_mutable_const::enums::BothOfCellAndGeneric::UNFROZEN_VARIANT` and
+ // `declare_interior_mutable_const::enums::BothOfCellAndGeneric::NO_ENUM`).
+ // One might be able to prevent these FNs correctly, and replace this with `false`;
+ // e.g. implementing `has_frozen_variant` described above, and not running this function
+ // when the type doesn't have any frozen variants would be the 'correct' way for the 2nd
+ // case (that actually removes another suboptimal behavior (I won't say 'false positive') where,
+ // similar to 2., but with the a frozen variant) (e.g. borrowing
+ // `borrow_interior_mutable_const::enums::AssocConsts::TO_BE_FROZEN_VARIANT`).
+ // I chose this way because unfrozen enums as assoc consts are rare (or, hopefully, none).
+ matches!(err, ErrorHandled::TooGeneric(..))
+ },
+ |val| val.map_or(true, |val| self.is_value_unfrozen_raw_inner(cx, val, ty)),
+ )
+ }
+
+ fn is_value_unfrozen_poly<'tcx>(&self, cx: &LateContext<'tcx>, body_id: BodyId, ty: Ty<'tcx>) -> bool {
+ let def_id = body_id.hir_id.owner.to_def_id();
+ let args = ty::GenericArgs::identity_for_item(cx.tcx, def_id);
+ let instance = ty::Instance::new(def_id, args);
+ let cid = rustc_middle::mir::interpret::GlobalId {
+ instance,
+ promoted: None,
+ };
+ let param_env = cx.tcx.param_env(def_id).with_reveal_all_normalized(cx.tcx);
+ let result = cx.tcx.const_eval_global_id_for_typeck(param_env, cid, None);
+ self.is_value_unfrozen_raw(cx, result, ty)
+ }
+
+ fn is_value_unfrozen_expr<'tcx>(&self, cx: &LateContext<'tcx>, hir_id: HirId, def_id: DefId, ty: Ty<'tcx>) -> bool {
+ let args = cx.typeck_results().node_args(hir_id);
+
+ let result = Self::const_eval_resolve(cx.tcx, cx.param_env, ty::UnevaluatedConst::new(def_id, args), None);
+ self.is_value_unfrozen_raw(cx, result, ty)
+ }
+
+ pub fn const_eval_resolve<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ param_env: ty::ParamEnv<'tcx>,
+ ct: ty::UnevaluatedConst<'tcx>,
+ span: Option<Span>,
+ ) -> EvalToValTreeResult<'tcx> {
+ match ty::Instance::resolve(tcx, param_env, ct.def, ct.args) {
+ Ok(Some(instance)) => {
+ let cid = GlobalId {
+ instance,
+ promoted: None,
+ };
+ tcx.const_eval_global_id_for_typeck(param_env, cid, span)
+ },
+ Ok(None) => Err(ErrorHandled::TooGeneric(span.unwrap_or(rustc_span::DUMMY_SP))),
+ Err(err) => Err(ErrorHandled::Reported(err.into(), span.unwrap_or(rustc_span::DUMMY_SP))),
+ }
+ }
+}
impl<'tcx> LateLintPass<'tcx> for NonCopyConst {
+ fn check_crate(&mut self, cx: &LateContext<'tcx>) {
+ self.ignore_mut_def_ids.clear();
+ let mut path = Vec::new();
+ for ty in &self.ignore_interior_mutability {
+ path.extend(ty.split("::"));
+ for id in def_path_def_ids(cx, &path[..]) {
+ self.ignore_mut_def_ids.insert(id);
+ }
+ path.clear();
+ }
+ }
+
fn check_item(&mut self, cx: &LateContext<'tcx>, it: &'tcx Item<'_>) {
if let ItemKind::Const(.., body_id) = it.kind {
let ty = cx.tcx.type_of(it.owner_id).instantiate_identity();
- if !ignored_macro(cx, it) && is_unfrozen(cx, ty) && is_value_unfrozen_poly(cx, body_id, ty) {
+ if !ignored_macro(cx, it)
+ && !self.is_ty_ignored(ty)
+ && Self::is_unfrozen(cx, ty)
+ && self.is_value_unfrozen_poly(cx, body_id, ty)
+ {
lint(cx, Source::Item { item: it.span });
}
}
@@ -311,7 +356,7 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst {
// Normalize assoc types because ones originated from generic params
// bounded other traits could have their bound.
let normalized = cx.tcx.normalize_erasing_regions(cx.param_env, ty);
- if is_unfrozen(cx, normalized)
+ if !self.is_ty_ignored(ty) && Self::is_unfrozen(cx, normalized)
// When there's no default value, lint it only according to its type;
// in other words, lint consts whose value *could* be unfrozen, not definitely is.
// This feels inconsistent with how the lint treats generic types,
@@ -324,7 +369,7 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst {
// i.e. having an enum doesn't necessary mean a type has a frozen variant.
// And, implementing it isn't a trivial task; it'll probably end up
// re-implementing the trait predicate evaluation specific to `Freeze`.
- && body_id_opt.map_or(true, |body_id| is_value_unfrozen_poly(cx, body_id, normalized))
+ && body_id_opt.map_or(true, |body_id| self.is_value_unfrozen_poly(cx, body_id, normalized))
{
lint(cx, Source::Assoc { item: trait_item.span });
}
@@ -367,8 +412,8 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst {
// e.g. `layout_of(...).is_err() || has_frozen_variant(...);`
let ty = cx.tcx.type_of(impl_item.owner_id).instantiate_identity();
let normalized = cx.tcx.normalize_erasing_regions(cx.param_env, ty);
- if is_unfrozen(cx, normalized);
- if is_value_unfrozen_poly(cx, *body_id, normalized);
+ if !self.is_ty_ignored(ty) && Self::is_unfrozen(cx, normalized);
+ if self.is_value_unfrozen_poly(cx, *body_id, normalized);
then {
lint(
cx,
@@ -384,7 +429,10 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst {
// Normalize assoc types originated from generic params.
let normalized = cx.tcx.normalize_erasing_regions(cx.param_env, ty);
- if is_unfrozen(cx, ty) && is_value_unfrozen_poly(cx, *body_id, normalized) {
+ if !self.is_ty_ignored(ty)
+ && Self::is_unfrozen(cx, ty)
+ && self.is_value_unfrozen_poly(cx, *body_id, normalized)
+ {
lint(cx, Source::Assoc { item: impl_item.span });
}
},
@@ -478,7 +526,10 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst {
cx.typeck_results().expr_ty(dereferenced_expr)
};
- if is_unfrozen(cx, ty) && is_value_unfrozen_expr(cx, expr.hir_id, item_def_id, ty) {
+ if !self.is_ty_ignored(ty)
+ && Self::is_unfrozen(cx, ty)
+ && self.is_value_unfrozen_expr(cx, expr.hir_id, item_def_id, ty)
+ {
lint(cx, Source::Expr { expr: expr.span });
}
}
diff --git a/src/tools/clippy/clippy_lints/src/non_expressive_names.rs b/src/tools/clippy/clippy_lints/src/non_expressive_names.rs
index d562047cb..61622034d 100644
--- a/src/tools/clippy/clippy_lints/src/non_expressive_names.rs
+++ b/src/tools/clippy/clippy_lints/src/non_expressive_names.rs
@@ -6,8 +6,7 @@ use rustc_ast::visit::{walk_block, walk_expr, walk_pat, Visitor};
use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
use rustc_session::{declare_tool_lint, impl_lint_pass};
-use rustc_span::source_map::Span;
-use rustc_span::sym;
+use rustc_span::{sym, Span};
use rustc_span::symbol::{Ident, Symbol};
use std::cmp::Ordering;
@@ -63,7 +62,7 @@ declare_clippy_lint! {
/// descriptive name.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let _1 = 1;
/// let ___1 = 1;
/// let __1___2 = 11;
diff --git a/src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs b/src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs
index e1de494eb..e94e45899 100644
--- a/src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs
+++ b/src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs
@@ -1,6 +1,5 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::{snippet_opt, snippet_with_applicability};
-use clippy_utils::ty::{is_type_diagnostic_item, match_type};
use clippy_utils::{match_def_path, paths};
use if_chain::if_chain;
use rustc_errors::Applicability;
@@ -45,15 +44,14 @@ impl<'tcx> LateLintPass<'tcx> for NonOctalUnixPermissions {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
match &expr.kind {
ExprKind::MethodCall(path, func, [param], _) => {
- let obj_ty = cx.typeck_results().expr_ty(func).peel_refs();
-
if_chain! {
+ if let Some(adt) = cx.typeck_results().expr_ty(func).peel_refs().ty_adt_def();
if (path.ident.name == sym!(mode)
- && (match_type(cx, obj_ty, &paths::OPEN_OPTIONS)
- || is_type_diagnostic_item(cx, obj_ty, sym::DirBuilder)))
- || (path.ident.name == sym!(set_mode) && match_type(cx, obj_ty, &paths::PERMISSIONS));
+ && matches!(cx.tcx.get_diagnostic_name(adt.did()), Some(sym::FsOpenOptions | sym::DirBuilder)))
+ || (path.ident.name == sym!(set_mode)
+ && cx.tcx.is_diagnostic_item(sym::FsPermissions, adt.did()));
if let ExprKind::Lit(_) = param.kind;
- if param.span.ctxt() == expr.span.ctxt();
+ if param.span.eq_ctxt(expr.span);
then {
let Some(snip) = snippet_opt(cx, param.span) else {
@@ -72,7 +70,7 @@ impl<'tcx> LateLintPass<'tcx> for NonOctalUnixPermissions {
if let Some(def_id) = cx.qpath_res(path, func.hir_id).opt_def_id();
if match_def_path(cx, def_id, &paths::PERMISSIONS_FROM_MODE);
if let ExprKind::Lit(_) = param.kind;
- if param.span.ctxt() == expr.span.ctxt();
+ if param.span.eq_ctxt(expr.span);
if let Some(snip) = snippet_opt(cx, param.span);
if !snip.starts_with("0o");
then {
diff --git a/src/tools/clippy/clippy_lints/src/non_send_fields_in_send_ty.rs b/src/tools/clippy/clippy_lints/src/non_send_fields_in_send_ty.rs
index c5e777c20..62ef48c8a 100644
--- a/src/tools/clippy/clippy_lints/src/non_send_fields_in_send_ty.rs
+++ b/src/tools/clippy/clippy_lints/src/non_send_fields_in_send_ty.rs
@@ -1,7 +1,7 @@
use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::is_lint_allowed;
use clippy_utils::source::snippet;
use clippy_utils::ty::{implements_trait, is_copy};
-use clippy_utils::{is_lint_allowed, match_def_path, paths};
use rustc_ast::ImplPolarity;
use rustc_hir::def_id::DefId;
use rustc_hir::{FieldDef, Item, ItemKind, Node};
@@ -233,7 +233,7 @@ fn contains_pointer_like<'tcx>(cx: &LateContext<'tcx>, target_ty: Ty<'tcx>) -> b
return true;
},
ty::Adt(adt_def, _) => {
- if match_def_path(cx, adt_def.did(), &paths::PTR_NON_NULL) {
+ if cx.tcx.is_diagnostic_item(sym::NonNull, adt_def.did()) {
return true;
}
},
diff --git a/src/tools/clippy/clippy_lints/src/nonstandard_macro_braces.rs b/src/tools/clippy/clippy_lints/src/nonstandard_macro_braces.rs
index bd194b935..11c3a5417 100644
--- a/src/tools/clippy/clippy_lints/src/nonstandard_macro_braces.rs
+++ b/src/tools/clippy/clippy_lints/src/nonstandard_macro_braces.rs
@@ -1,6 +1,4 @@
-use std::fmt;
-use std::hash::{Hash, Hasher};
-
+use clippy_config::types::MacroMatcher;
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_opt;
use if_chain::if_chain;
@@ -12,7 +10,6 @@ use rustc_lint::{EarlyContext, EarlyLintPass};
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::hygiene::{ExpnKind, MacroKind};
use rustc_span::Span;
-use serde::{de, Deserialize};
declare_clippy_lint! {
/// ### What it does
@@ -23,11 +20,11 @@ declare_clippy_lint! {
/// doesn't give you a semicolon in item position, which can be unexpected.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// vec!{1, 2, 3};
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// vec![1, 2, 3];
/// ```
#[clippy::version = "1.55.0"]
@@ -36,8 +33,6 @@ declare_clippy_lint! {
"check consistent use of braces in macro"
}
-const BRACES: &[(&str, &str)] = &[("(", ")"), ("{", "}"), ("[", "]")];
-
/// The (callsite span, (open brace, close brace), source snippet)
type MacroInfo<'a> = (Span, &'a (String, String), String);
@@ -195,81 +190,3 @@ macro_rules! macro_matcher {
};
}
pub(crate) use macro_matcher;
-
-#[derive(Clone, Debug)]
-pub struct MacroMatcher {
- name: String,
- braces: (String, String),
-}
-
-impl Hash for MacroMatcher {
- fn hash<H: Hasher>(&self, state: &mut H) {
- self.name.hash(state);
- }
-}
-
-impl PartialEq for MacroMatcher {
- fn eq(&self, other: &Self) -> bool {
- self.name == other.name
- }
-}
-impl Eq for MacroMatcher {}
-
-impl<'de> Deserialize<'de> for MacroMatcher {
- fn deserialize<D>(deser: D) -> Result<Self, D::Error>
- where
- D: de::Deserializer<'de>,
- {
- #[derive(Deserialize)]
- #[serde(field_identifier, rename_all = "lowercase")]
- enum Field {
- Name,
- Brace,
- }
- struct MacVisitor;
- impl<'de> de::Visitor<'de> for MacVisitor {
- type Value = MacroMatcher;
-
- fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
- formatter.write_str("struct MacroMatcher")
- }
-
- fn visit_map<V>(self, mut map: V) -> Result<Self::Value, V::Error>
- where
- V: de::MapAccess<'de>,
- {
- let mut name = None;
- let mut brace: Option<String> = None;
- while let Some(key) = map.next_key()? {
- match key {
- Field::Name => {
- if name.is_some() {
- return Err(de::Error::duplicate_field("name"));
- }
- name = Some(map.next_value()?);
- },
- Field::Brace => {
- if brace.is_some() {
- return Err(de::Error::duplicate_field("brace"));
- }
- brace = Some(map.next_value()?);
- },
- }
- }
- let name = name.ok_or_else(|| de::Error::missing_field("name"))?;
- let brace = brace.ok_or_else(|| de::Error::missing_field("brace"))?;
- Ok(MacroMatcher {
- name,
- braces: BRACES
- .iter()
- .find(|b| b.0 == brace)
- .map(|(o, c)| ((*o).to_owned(), (*c).to_owned()))
- .ok_or_else(|| de::Error::custom(format!("expected one of `(`, `{{`, `[` found `{brace}`")))?,
- })
- }
- }
-
- const FIELDS: &[&str] = &["name", "brace"];
- deser.deserialize_struct("MacroMatcher", FIELDS, MacVisitor)
- }
-}
diff --git a/src/tools/clippy/clippy_lints/src/octal_escapes.rs b/src/tools/clippy/clippy_lints/src/octal_escapes.rs
index 6d3865080..0faf4ce3d 100644
--- a/src/tools/clippy/clippy_lints/src/octal_escapes.rs
+++ b/src/tools/clippy/clippy_lints/src/octal_escapes.rs
@@ -32,13 +32,13 @@ declare_clippy_lint! {
/// can see it.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let one = "\033[1m Bold? \033[0m"; // \033 intended as escape
/// let two = "\033\0"; // \033 intended as null-3-3
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// let one = "\x1b[1mWill this be bold?\x1b[0m";
/// let two = "\x0033\x00";
/// ```
diff --git a/src/tools/clippy/clippy_lints/src/only_used_in_recursion.rs b/src/tools/clippy/clippy_lints/src/only_used_in_recursion.rs
index 3dc652f9d..ef7b36764 100644
--- a/src/tools/clippy/clippy_lints/src/only_used_in_recursion.rs
+++ b/src/tools/clippy/clippy_lints/src/only_used_in_recursion.rs
@@ -30,7 +30,7 @@ declare_clippy_lint! {
///
/// In some cases, this would not catch all useless arguments.
///
- /// ```rust
+ /// ```no_run
/// fn foo(a: usize, b: usize) -> usize {
/// let f = |x| x + 1;
///
@@ -53,7 +53,7 @@ declare_clippy_lint! {
/// Also, when you recurse the function name with path segments, it is not possible to detect.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// fn f(a: usize, b: usize) -> usize {
/// if a == 0 {
/// 1
@@ -66,7 +66,7 @@ declare_clippy_lint! {
/// # }
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// fn f(a: usize) -> usize {
/// if a == 0 {
/// 1
@@ -134,6 +134,7 @@ impl Usage {
/// The parameters being checked by the lint, indexed by both the parameter's `HirId` and the
/// `DefId` of the function paired with the parameter's index.
#[derive(Default)]
+#[allow(clippy::struct_field_names)]
struct Params {
params: Vec<Param>,
by_id: HirIdMap<usize>,
@@ -243,7 +244,10 @@ impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion {
})) => {
#[allow(trivial_casts)]
if let Some(Node::Item(item)) = get_parent_node(cx.tcx, owner_id.into())
- && let Some(trait_ref) = cx.tcx.impl_trait_ref(item.owner_id).map(EarlyBinder::instantiate_identity)
+ && let Some(trait_ref) = cx
+ .tcx
+ .impl_trait_ref(item.owner_id)
+ .map(EarlyBinder::instantiate_identity)
&& let Some(trait_item_id) = cx.tcx.associated_item(owner_id).trait_item_def_id
{
(
@@ -287,8 +291,7 @@ impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion {
// Recursive call. Track which index the parameter is used in.
ExprKind::Call(callee, args)
if path_def_id(cx, callee).map_or(false, |id| {
- id == param.fn_id
- && has_matching_args(param.fn_kind, typeck.node_args(callee.hir_id))
+ id == param.fn_id && has_matching_args(param.fn_kind, typeck.node_args(callee.hir_id))
}) =>
{
if let Some(idx) = args.iter().position(|arg| arg.hir_id == child_id) {
@@ -298,8 +301,7 @@ impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion {
},
ExprKind::MethodCall(_, receiver, args, _)
if typeck.type_dependent_def_id(parent.hir_id).map_or(false, |id| {
- id == param.fn_id
- && has_matching_args(param.fn_kind, typeck.node_args(parent.hir_id))
+ id == param.fn_id && has_matching_args(param.fn_kind, typeck.node_args(parent.hir_id))
}) =>
{
if let Some(idx) = iter::once(receiver).chain(args).position(|arg| arg.hir_id == child_id) {
@@ -335,8 +337,8 @@ impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion {
// Only allow field accesses without auto-deref
ExprKind::Field(..) if typeck.adjustments().get(child_id).is_none() => {
e = parent;
- continue
- }
+ continue;
+ },
_ => (),
},
_ => (),
diff --git a/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs b/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs
index a10aa65e5..4c6462b77 100644
--- a/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs
+++ b/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs
@@ -7,9 +7,9 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::Ty;
use rustc_session::impl_lint_pass;
-use rustc_span::source_map::{Span, Spanned};
+use rustc_span::{Span, Symbol};
+use rustc_span::source_map::Spanned;
use rustc_span::symbol::sym;
-use rustc_span::Symbol;
use {rustc_ast as ast, rustc_hir as hir};
const HARD_CODED_ALLOWED_BINARY: &[[&str; 2]] = &[["f32", "f32"], ["f64", "f64"], ["std::string::String", "str"]];
@@ -71,7 +71,7 @@ impl ArithmeticSideEffects {
rhs_has_allowed_ty || rhs_from_specific.contains("*")
}
{
- true
+ true
} else if let Some(rhs_from_glob) = self.allowed_binary.get("*") {
rhs_from_glob.contains(rhs_ty_string_elem)
} else {
@@ -132,7 +132,11 @@ impl ArithmeticSideEffects {
}
// Common entry-point to avoid code duplication.
- fn issue_lint(&mut self, cx: &LateContext<'_>, expr: &hir::Expr<'_>) {
+ fn issue_lint<'tcx>(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
+ if is_from_proc_macro(cx, expr) {
+ return;
+ }
+
let msg = "arithmetic operation that can potentially result in unexpected side-effects";
span_lint(cx, ARITHMETIC_SIDE_EFFECTS, expr.span, msg);
self.expr_span = Some(expr.span);
@@ -144,8 +148,10 @@ impl ArithmeticSideEffects {
/// like `i32::MAX` or constant references like `N` from `const N: i32 = 1;`,
fn literal_integer(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<u128> {
let actual = peel_hir_expr_unary(expr).0;
- if let hir::ExprKind::Lit(lit) = actual.kind && let ast::LitKind::Int(n, _) = lit.node {
- return Some(n)
+ if let hir::ExprKind::Lit(lit) = actual.kind
+ && let ast::LitKind::Int(n, _) = lit.node
+ {
+ return Some(n);
}
if let Some(Constant::Int(n)) = constant(cx, cx.typeck_results(), expr) {
return Some(n);
@@ -158,10 +164,10 @@ impl ArithmeticSideEffects {
fn manage_bin_ops<'tcx>(
&mut self,
cx: &LateContext<'tcx>,
- expr: &hir::Expr<'tcx>,
+ expr: &'tcx hir::Expr<'_>,
op: &Spanned<hir::BinOpKind>,
- lhs: &hir::Expr<'tcx>,
- rhs: &hir::Expr<'tcx>,
+ lhs: &'tcx hir::Expr<'_>,
+ rhs: &'tcx hir::Expr<'_>,
) {
if constant_simple(cx, cx.typeck_results(), expr).is_some() {
return;
@@ -234,10 +240,10 @@ impl ArithmeticSideEffects {
/// provided input.
fn manage_method_call<'tcx>(
&mut self,
- args: &[hir::Expr<'tcx>],
+ args: &'tcx [hir::Expr<'_>],
cx: &LateContext<'tcx>,
- ps: &hir::PathSegment<'tcx>,
- receiver: &hir::Expr<'tcx>,
+ ps: &'tcx hir::PathSegment<'_>,
+ receiver: &'tcx hir::Expr<'_>,
) {
let Some(arg) = args.first() else {
return;
@@ -262,8 +268,8 @@ impl ArithmeticSideEffects {
fn manage_unary_ops<'tcx>(
&mut self,
cx: &LateContext<'tcx>,
- expr: &hir::Expr<'tcx>,
- un_expr: &hir::Expr<'tcx>,
+ expr: &'tcx hir::Expr<'_>,
+ un_expr: &'tcx hir::Expr<'_>,
un_op: hir::UnOp,
) {
let hir::UnOp::Neg = un_op else {
@@ -285,14 +291,13 @@ impl ArithmeticSideEffects {
fn should_skip_expr<'tcx>(&mut self, cx: &LateContext<'tcx>, expr: &hir::Expr<'tcx>) -> bool {
is_lint_allowed(cx, ARITHMETIC_SIDE_EFFECTS, expr.hir_id)
- || is_from_proc_macro(cx, expr)
|| self.expr_span.is_some()
|| self.const_span.map_or(false, |sp| sp.contains(expr.span))
}
}
impl<'tcx> LateLintPass<'tcx> for ArithmeticSideEffects {
- fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &hir::Expr<'tcx>) {
+ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) {
if self.should_skip_expr(cx, expr) {
return;
}
@@ -317,7 +322,9 @@ impl<'tcx> LateLintPass<'tcx> for ArithmeticSideEffects {
let body_owner_kind = cx.tcx.hir().body_owner_kind(body_owner_def_id);
if let hir::BodyOwnerKind::Const { .. } | hir::BodyOwnerKind::Static(_) = body_owner_kind {
let body_span = cx.tcx.hir().span_with_body(body_owner);
- if let Some(span) = self.const_span && span.contains(body_span) {
+ if let Some(span) = self.const_span
+ && span.contains(body_span)
+ {
return;
}
self.const_span = Some(body_span);
@@ -327,7 +334,9 @@ impl<'tcx> LateLintPass<'tcx> for ArithmeticSideEffects {
fn check_body_post(&mut self, cx: &LateContext<'_>, body: &hir::Body<'_>) {
let body_owner = cx.tcx.hir().body_owner(body.id());
let body_span = cx.tcx.hir().span(body_owner);
- if let Some(span) = self.const_span && span.contains(body_span) {
+ if let Some(span) = self.const_span
+ && span.contains(body_span)
+ {
return;
}
self.const_span = None;
diff --git a/src/tools/clippy/clippy_lints/src/operators/bit_mask.rs b/src/tools/clippy/clippy_lints/src/operators/bit_mask.rs
index c146f3ae9..2e026c369 100644
--- a/src/tools/clippy/clippy_lints/src/operators/bit_mask.rs
+++ b/src/tools/clippy/clippy_lints/src/operators/bit_mask.rs
@@ -2,7 +2,7 @@ use clippy_utils::consts::{constant, Constant};
use clippy_utils::diagnostics::span_lint;
use rustc_hir::{BinOpKind, Expr, ExprKind};
use rustc_lint::LateContext;
-use rustc_span::source_map::Span;
+use rustc_span::Span;
use super::{BAD_BIT_MASK, INEFFECTIVE_BIT_MASK};
diff --git a/src/tools/clippy/clippy_lints/src/operators/cmp_owned.rs b/src/tools/clippy/clippy_lints/src/operators/cmp_owned.rs
index d3de9699f..ea8ed28ba 100644
--- a/src/tools/clippy/clippy_lints/src/operators/cmp_owned.rs
+++ b/src/tools/clippy/clippy_lints/src/operators/cmp_owned.rs
@@ -1,7 +1,7 @@
use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::path_def_id;
use clippy_utils::source::snippet;
use clippy_utils::ty::{implements_trait, is_copy};
-use clippy_utils::{match_def_path, path_def_id, paths};
use rustc_errors::Applicability;
use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp};
use rustc_lint::LateContext;
@@ -50,7 +50,7 @@ fn check_op(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>, left: bool)
},
ExprKind::Call(path, [arg])
if path_def_id(cx, path).map_or(false, |did| {
- if match_def_path(cx, did, &paths::FROM_STR_METHOD) {
+ if cx.tcx.is_diagnostic_item(sym::from_str_method, did) {
true
} else if cx.tcx.is_diagnostic_item(sym::from_fn, did) {
!is_copy(cx, typeck.expr_ty(expr))
diff --git a/src/tools/clippy/clippy_lints/src/operators/const_comparisons.rs b/src/tools/clippy/clippy_lints/src/operators/const_comparisons.rs
index abe8df195..ec2bb8699 100644
--- a/src/tools/clippy/clippy_lints/src/operators/const_comparisons.rs
+++ b/src/tools/clippy/clippy_lints/src/operators/const_comparisons.rs
@@ -8,7 +8,8 @@ use rustc_hir::{BinOpKind, Expr, ExprKind};
use rustc_lint::LateContext;
use rustc_middle::ty::layout::HasTyCtxt;
use rustc_middle::ty::{Ty, TypeckResults};
-use rustc_span::source_map::{Span, Spanned};
+use rustc_span::Span;
+use rustc_span::source_map::Spanned;
use clippy_utils::diagnostics::span_lint_and_note;
use clippy_utils::source::snippet;
diff --git a/src/tools/clippy/clippy_lints/src/operators/double_comparison.rs b/src/tools/clippy/clippy_lints/src/operators/double_comparison.rs
index 56a86d0ff..d48e8286f 100644
--- a/src/tools/clippy/clippy_lints/src/operators/double_comparison.rs
+++ b/src/tools/clippy/clippy_lints/src/operators/double_comparison.rs
@@ -4,7 +4,7 @@ use clippy_utils::source::snippet_with_applicability;
use rustc_errors::Applicability;
use rustc_hir::{BinOpKind, Expr, ExprKind};
use rustc_lint::LateContext;
-use rustc_span::source_map::Span;
+use rustc_span::Span;
use super::DOUBLE_COMPARISONS;
diff --git a/src/tools/clippy/clippy_lints/src/operators/eq_op.rs b/src/tools/clippy/clippy_lints/src/operators/eq_op.rs
index 88d566318..fd3502ad8 100644
--- a/src/tools/clippy/clippy_lints/src/operators/eq_op.rs
+++ b/src/tools/clippy/clippy_lints/src/operators/eq_op.rs
@@ -8,13 +8,14 @@ use rustc_lint::LateContext;
use super::EQ_OP;
pub(crate) fn check_assert<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
- if let Some((macro_call, macro_name))
- = first_node_macro_backtrace(cx, e).find_map(|macro_call| {
- let name = cx.tcx.item_name(macro_call.def_id);
- matches!(name.as_str(), "assert_eq" | "assert_ne" | "debug_assert_eq" | "debug_assert_ne")
- .then(|| (macro_call, name))
- })
- && let Some((lhs, rhs, _)) = find_assert_eq_args(cx, e, macro_call.expn)
+ if let Some((macro_call, macro_name)) = first_node_macro_backtrace(cx, e).find_map(|macro_call| {
+ let name = cx.tcx.item_name(macro_call.def_id);
+ matches!(
+ name.as_str(),
+ "assert_eq" | "assert_ne" | "debug_assert_eq" | "debug_assert_ne"
+ )
+ .then(|| (macro_call, name))
+ }) && let Some((lhs, rhs, _)) = find_assert_eq_args(cx, e, macro_call.expn)
&& eq_expr_value(cx, lhs, rhs)
&& macro_call.is_local()
&& !is_in_test_function(cx.tcx, e.hir_id)
@@ -42,7 +43,9 @@ pub(crate) fn check<'tcx>(
e.span,
&format!("equal expressions as operands to `{}`", op.as_str()),
|diag| {
- if let BinOpKind::Ne = op && cx.typeck_results().expr_ty(left).is_floating_point() {
+ if let BinOpKind::Ne = op
+ && cx.typeck_results().expr_ty(left).is_floating_point()
+ {
diag.note("if you intended to check if the operand is NaN, use `.is_nan()` instead");
}
},
diff --git a/src/tools/clippy/clippy_lints/src/operators/identity_op.rs b/src/tools/clippy/clippy_lints/src/operators/identity_op.rs
index 14a12da86..8ecb03862 100644
--- a/src/tools/clippy/clippy_lints/src/operators/identity_op.rs
+++ b/src/tools/clippy/clippy_lints/src/operators/identity_op.rs
@@ -6,7 +6,7 @@ use rustc_errors::Applicability;
use rustc_hir::{BinOpKind, Expr, ExprKind, Node};
use rustc_lint::LateContext;
use rustc_middle::ty;
-use rustc_span::source_map::Span;
+use rustc_span::Span;
use super::IDENTITY_OP;
diff --git a/src/tools/clippy/clippy_lints/src/operators/mod.rs b/src/tools/clippy/clippy_lints/src/operators/mod.rs
index 4635e1164..ee79ea276 100644
--- a/src/tools/clippy/clippy_lints/src/operators/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/operators/mod.rs
@@ -48,7 +48,7 @@ declare_clippy_lint! {
/// like `#[cfg(target_pointer_width = "64")] ..` instead.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let vec: Vec<isize> = Vec::new();
/// if vec.len() <= 0 {}
/// if 100 > i32::MAX {}
@@ -76,7 +76,7 @@ declare_clippy_lint! {
/// desirable to explicitly call checked, wrapping or saturating arithmetic methods.
///
/// #### Example
- /// ```rust
+ /// ```no_run
/// // `n` can be any number, including `i32::MAX`.
/// fn foo(n: i32) -> i32 {
/// n + 1
@@ -105,7 +105,7 @@ declare_clippy_lint! {
/// can be useful to rule out floating-point numbers.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # let a = 0.0;
/// a + 1.0;
/// ```
@@ -128,7 +128,7 @@ declare_clippy_lint! {
/// implementations that differ from the regular `Op` impl.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let mut a = 5;
/// let b = 0;
/// // ...
@@ -137,7 +137,7 @@ declare_clippy_lint! {
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// let mut a = 5;
/// let b = 0;
/// // ...
@@ -165,7 +165,7 @@ declare_clippy_lint! {
/// written as `a = a op a op b` as it's less confusing.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let mut a = 5;
/// let b = 2;
/// // ...
@@ -205,7 +205,7 @@ declare_clippy_lint! {
/// test-case for this lint.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # let x = 1;
/// if (x & 1 == 2) { }
/// ```
@@ -238,7 +238,7 @@ declare_clippy_lint! {
/// uncommon).
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # let x = 1;
/// if (x | 1 > 3) { }
/// ```
@@ -261,7 +261,7 @@ declare_clippy_lint! {
/// llvm generates better code for `x & 15 == 0` on x86
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # let x = 1;
/// if x & 0b1111 == 0 { }
/// ```
@@ -280,7 +280,7 @@ declare_clippy_lint! {
/// Readability.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # let x = 1;
/// # let y = 2;
/// if x == y || x < y {}
@@ -288,7 +288,7 @@ declare_clippy_lint! {
///
/// Use instead:
///
- /// ```rust
+ /// ```no_run
/// # let x = 1;
/// # let y = 2;
/// if x <= y {}
@@ -308,11 +308,11 @@ declare_clippy_lint! {
/// which is probably not the programmer's intention
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # let status_code = 200;
/// if status_code <= 400 && status_code > 500 {}
/// ```
- #[clippy::version = "1.71.0"]
+ #[clippy::version = "1.73.0"]
pub IMPOSSIBLE_COMPARISONS,
correctness,
"double comparisons that will never evaluate to `true`"
@@ -328,11 +328,11 @@ declare_clippy_lint! {
/// different value entirely.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # let status_code = 200;
/// if status_code <= 400 && status_code < 500 {}
/// ```
- #[clippy::version = "1.71.0"]
+ #[clippy::version = "1.73.0"]
pub REDUNDANT_COMPARISONS,
correctness,
"double comparisons where one of them can be removed"
@@ -348,7 +348,7 @@ declare_clippy_lint! {
/// `Duration::subsec_millis()` than to calculate them.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # use std::time::Duration;
/// # let duration = Duration::new(5, 0);
/// let micros = duration.subsec_nanos() / 1_000;
@@ -356,7 +356,7 @@ declare_clippy_lint! {
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # use std::time::Duration;
/// # let duration = Duration::new(5, 0);
/// let micros = duration.subsec_micros();
@@ -384,7 +384,7 @@ declare_clippy_lint! {
/// calls. We may introduce a list of known pure functions in the future.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # let x = 1;
/// if x + 1 == x + 1 {}
///
@@ -434,7 +434,7 @@ declare_clippy_lint! {
/// corrected
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let x = 1;
/// 0 / x;
/// 0 * x;
@@ -462,13 +462,13 @@ declare_clippy_lint! {
/// with an allow.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// pub fn is_roughly_equal(a: f32, b: f32) -> bool {
/// (a - b) < f32::EPSILON
/// }
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// pub fn is_roughly_equal(a: f32, b: f32) -> bool {
/// (a - b).abs() < f32::EPSILON
/// }
@@ -488,7 +488,7 @@ declare_clippy_lint! {
/// meaning. So it just obscures what's going on. Delete it mercilessly.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # let x = 1;
/// x / 1 + 0 * 1 - 0 | 0;
/// ```
@@ -508,13 +508,13 @@ declare_clippy_lint! {
/// remainder.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let x = 3 / 2;
/// println!("{}", x);
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// let x = 3f32 / 2f32;
/// println!("{}", x);
/// ```
@@ -535,14 +535,14 @@ declare_clippy_lint! {
/// needlessly consuming code and heap space.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # let x = "foo";
/// # let y = String::from("foo");
/// if x.to_owned() == y {}
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # let x = "foo";
/// # let y = String::from("foo");
/// if x == y {}
@@ -566,7 +566,7 @@ declare_clippy_lint! {
/// guide](http://www.floating-point-gui.de/errors/comparison).
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let x = 1.2331f64;
/// let y = 1.2332f64;
///
@@ -575,7 +575,7 @@ declare_clippy_lint! {
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # let x = 1.2331f64;
/// # let y = 1.2332f64;
/// let error_margin = f64::EPSILON; // Use an epsilon for comparison
@@ -603,7 +603,7 @@ declare_clippy_lint! {
/// guide](http://www.floating-point-gui.de/errors/comparison).
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let x: f64 = 1.0;
/// const ONE: f64 = 1.00;
///
@@ -611,7 +611,7 @@ declare_clippy_lint! {
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # let x: f64 = 1.0;
/// # const ONE: f64 = 1.00;
/// let error_margin = f64::EPSILON; // Use an epsilon for comparison
@@ -638,7 +638,7 @@ declare_clippy_lint! {
/// contest, it's probably a bad idea. Use something more underhanded.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # let x = 1;
/// let a = x % 1;
/// let a = x % -1;
@@ -662,7 +662,7 @@ declare_clippy_lint! {
/// For example, in Rust `17 % -3 = 2`, but in Python `17 % -3 = -1`.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let x = -17 % 3;
/// ```
#[clippy::version = "1.42.0"]
@@ -685,12 +685,12 @@ declare_clippy_lint! {
/// determination is quite conservative.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let (x,y) = (true, false);
/// if x & !y {} // where both x and y are booleans
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// let (x,y) = (true, false);
/// if x && !y {}
/// ```
@@ -710,14 +710,14 @@ declare_clippy_lint! {
/// comparing the values they point to.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let a = &[1, 2, 3];
/// let b = &[1, 2, 3];
///
/// assert!(a as *const _ as usize == b as *const _ as usize);
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// let a = &[1, 2, 3];
/// let b = &[1, 2, 3];
///
@@ -742,7 +742,7 @@ declare_clippy_lint! {
/// indexing operations they are assumed not to have any side effects.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// struct Event {
/// x: i32,
/// }
@@ -753,7 +753,7 @@ declare_clippy_lint! {
/// ```
///
/// Should be:
- /// ```rust
+ /// ```no_run
/// struct Event {
/// x: i32,
/// }
diff --git a/src/tools/clippy/clippy_lints/src/operators/numeric_arithmetic.rs b/src/tools/clippy/clippy_lints/src/operators/numeric_arithmetic.rs
index 80389cbf8..ea933168c 100644
--- a/src/tools/clippy/clippy_lints/src/operators/numeric_arithmetic.rs
+++ b/src/tools/clippy/clippy_lints/src/operators/numeric_arithmetic.rs
@@ -3,7 +3,7 @@ use clippy_utils::consts::constant_simple;
use clippy_utils::diagnostics::span_lint;
use rustc_hir as hir;
use rustc_lint::LateContext;
-use rustc_span::source_map::Span;
+use rustc_span::Span;
#[derive(Default)]
pub struct Context {
diff --git a/src/tools/clippy/clippy_lints/src/option_env_unwrap.rs b/src/tools/clippy/clippy_lints/src/option_env_unwrap.rs
index 9c7f7e1cd..7792efe6a 100644
--- a/src/tools/clippy/clippy_lints/src/option_env_unwrap.rs
+++ b/src/tools/clippy/clippy_lints/src/option_env_unwrap.rs
@@ -46,16 +46,19 @@ impl EarlyLintPass for OptionEnvUnwrap {
);
}
- if let ExprKind::MethodCall(box MethodCall { seg, receiver, .. }) = &expr.kind &&
- matches!(seg.ident.name, sym::expect | sym::unwrap) {
- if let ExprKind::Call(caller, _) = &receiver.kind &&
+ if let ExprKind::MethodCall(box MethodCall { seg, receiver, .. }) = &expr.kind
+ && matches!(seg.ident.name, sym::expect | sym::unwrap)
+ {
+ if let ExprKind::Call(caller, _) = &receiver.kind &&
// If it exists, it will be ::core::option::Option::Some("<env var>").unwrap() (A method call in the HIR)
- is_direct_expn_of(caller.span, "option_env").is_some() {
- lint(cx, expr.span);
- } else if let ExprKind::Path(_, caller) = &receiver.kind && // If it doesn't exist, it will be ::core::option::Option::None::<&'static str>.unwrap() (A path in the HIR)
- is_direct_expn_of(caller.span, "option_env").is_some() {
- lint(cx, expr.span);
- }
- }
+ is_direct_expn_of(caller.span, "option_env").is_some()
+ {
+ lint(cx, expr.span);
+ } else if let ExprKind::Path(_, caller) = &receiver.kind && // If it doesn't exist, it will be ::core::option::Option::None::<&'static str>.unwrap() (A path in the HIR)
+ is_direct_expn_of(caller.span, "option_env").is_some()
+ {
+ lint(cx, expr.span);
+ }
+ }
}
}
diff --git a/src/tools/clippy/clippy_lints/src/option_if_let_else.rs b/src/tools/clippy/clippy_lints/src/option_if_let_else.rs
index a7a7f4fd8..d7cbbe13a 100644
--- a/src/tools/clippy/clippy_lints/src/option_if_let_else.rs
+++ b/src/tools/clippy/clippy_lints/src/option_if_let_else.rs
@@ -32,7 +32,7 @@ declare_clippy_lint! {
/// this lint will not be raised.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # let optional: Option<u32> = Some(0);
/// # fn do_complicated_function() -> u32 { 5 };
/// let _ = if let Some(foo) = optional {
@@ -54,7 +54,7 @@ declare_clippy_lint! {
///
/// should be
///
- /// ```rust
+ /// ```no_run
/// # let optional: Option<u32> = Some(0);
/// # fn do_complicated_function() -> u32 { 5 };
/// let _ = optional.map_or(5, |foo| foo);
@@ -165,6 +165,12 @@ fn try_get_option_occurrence<'tcx>(
}
let mut app = Applicability::Unspecified;
+
+ let (none_body, is_argless_call) = match none_body.kind {
+ ExprKind::Call(call_expr, []) if !none_body.span.from_expansion() => (call_expr, true),
+ _ => (none_body, false),
+ };
+
return Some(OptionOccurrence {
option: format_option_in_sugg(
Sugg::hir_with_context(cx, cond_expr, ctxt, "..", &mut app),
@@ -178,7 +184,7 @@ fn try_get_option_occurrence<'tcx>(
),
none_expr: format!(
"{}{}",
- if method_sugg == "map_or" { "" } else if is_result { "|_| " } else { "|| "},
+ if method_sugg == "map_or" || is_argless_call { "" } else if is_result { "|_| " } else { "|| "},
Sugg::hir_with_context(cx, none_body, ctxt, "..", &mut app),
),
});
@@ -234,7 +240,7 @@ fn try_convert_match<'tcx>(
if let [first_arm, second_arm] = arms
&& first_arm.guard.is_none()
&& second_arm.guard.is_none()
- {
+ {
return if is_none_or_err_arm(cx, second_arm) {
Some((first_arm.pat, first_arm.body, second_arm.body))
} else if is_none_or_err_arm(cx, first_arm) {
diff --git a/src/tools/clippy/clippy_lints/src/overflow_check_conditional.rs b/src/tools/clippy/clippy_lints/src/overflow_check_conditional.rs
index 6dabbd480..38cd5043a 100644
--- a/src/tools/clippy/clippy_lints/src/overflow_check_conditional.rs
+++ b/src/tools/clippy/clippy_lints/src/overflow_check_conditional.rs
@@ -14,7 +14,7 @@ declare_clippy_lint! {
/// Rust. Users can use functions like `overflowing_*` and `wrapping_*` instead.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # let a = 1;
/// # let b = 2;
/// a + b < a;
diff --git a/src/tools/clippy/clippy_lints/src/panic_in_result_fn.rs b/src/tools/clippy/clippy_lints/src/panic_in_result_fn.rs
index a049427d8..6a760f9fe 100644
--- a/src/tools/clippy/clippy_lints/src/panic_in_result_fn.rs
+++ b/src/tools/clippy/clippy_lints/src/panic_in_result_fn.rs
@@ -22,14 +22,14 @@ declare_clippy_lint! {
/// Functions called from a function returning a `Result` may invoke a panicking macro. This is not checked.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// fn result_with_panic() -> Result<bool, String>
/// {
/// panic!("error");
/// }
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// fn result_without_panic() -> Result<bool, String> {
/// Err(String::from("error"))
/// }
diff --git a/src/tools/clippy/clippy_lints/src/panic_unimplemented.rs b/src/tools/clippy/clippy_lints/src/panic_unimplemented.rs
index a72aefe91..f4f1f6ddb 100644
--- a/src/tools/clippy/clippy_lints/src/panic_unimplemented.rs
+++ b/src/tools/clippy/clippy_lints/src/panic_unimplemented.rs
@@ -9,7 +9,7 @@ declare_clippy_lint! {
/// Checks for usage of `panic!`.
///
/// ### Why is this bad?
- /// `panic!` will stop the execution of the executable
+ /// `panic!` will stop the execution of the executable.
///
/// ### Example
/// ```no_run
@@ -26,7 +26,7 @@ declare_clippy_lint! {
/// Checks for usage of `unimplemented!`.
///
/// ### Why is this bad?
- /// This macro should not be present in production code
+ /// This macro should not be present in production code.
///
/// ### Example
/// ```no_run
@@ -43,12 +43,17 @@ declare_clippy_lint! {
/// Checks for usage of `todo!`.
///
/// ### Why is this bad?
- /// This macro should not be present in production code
+ /// The `todo!` macro is often used for unfinished code, and it causes
+ /// code to panic. It should not be present in production code.
///
/// ### Example
/// ```no_run
/// todo!();
/// ```
+ /// Finish the implementation, or consider marking it as explicitly unimplemented.
+ /// ```no_run
+ /// unimplemented!();
+ /// ```
#[clippy::version = "1.40.0"]
pub TODO,
restriction,
@@ -60,7 +65,7 @@ declare_clippy_lint! {
/// Checks for usage of `unreachable!`.
///
/// ### Why is this bad?
- /// This macro can cause code to panic
+ /// This macro can cause code to panic.
///
/// ### Example
/// ```no_run
diff --git a/src/tools/clippy/clippy_lints/src/partial_pub_fields.rs b/src/tools/clippy/clippy_lints/src/partial_pub_fields.rs
index f60d9d65b..99ba55b6b 100644
--- a/src/tools/clippy/clippy_lints/src/partial_pub_fields.rs
+++ b/src/tools/clippy/clippy_lints/src/partial_pub_fields.rs
@@ -16,7 +16,7 @@ declare_clippy_lint! {
/// * Data: relatively simple objects which group a bunch of related attributes together.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// pub struct Color {
/// pub r: u8,
/// pub g: u8,
@@ -24,7 +24,7 @@ declare_clippy_lint! {
/// }
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// pub struct Color {
/// pub r: u8,
/// pub g: u8,
diff --git a/src/tools/clippy/clippy_lints/src/partialeq_ne_impl.rs b/src/tools/clippy/clippy_lints/src/partialeq_ne_impl.rs
index a8c4823fe..68d3d00ac 100644
--- a/src/tools/clippy/clippy_lints/src/partialeq_ne_impl.rs
+++ b/src/tools/clippy/clippy_lints/src/partialeq_ne_impl.rs
@@ -16,7 +16,7 @@ declare_clippy_lint! {
/// re-implement it.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// struct Foo;
///
/// impl PartialEq for Foo {
diff --git a/src/tools/clippy/clippy_lints/src/partialeq_to_none.rs b/src/tools/clippy/clippy_lints/src/partialeq_to_none.rs
index d9f5d1642..11e9a2bc3 100644
--- a/src/tools/clippy/clippy_lints/src/partialeq_to_none.rs
+++ b/src/tools/clippy/clippy_lints/src/partialeq_to_none.rs
@@ -21,13 +21,13 @@ declare_clippy_lint! {
/// way relies on `T: PartialEq` to do the comparison, which is unneeded.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// fn foo(f: Option<u32>) -> &'static str {
/// if f != None { "yay" } else { "nay" }
/// }
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// fn foo(f: Option<u32>) -> &'static str {
/// if f.is_some() { "yay" } else { "nay" }
/// }
diff --git a/src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs b/src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs
index 41513647f..4d7a055da 100644
--- a/src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs
+++ b/src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs
@@ -58,12 +58,12 @@ declare_clippy_lint! {
///
/// ### Example
///
- /// ```rust
+ /// ```no_run
/// fn foo(v: &u32) {}
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// fn foo(v: u32) {}
/// ```
#[clippy::version = "pre 1.29.0"]
@@ -86,7 +86,7 @@ declare_clippy_lint! {
/// `memcpy`, which can be expensive.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// #[derive(Clone, Copy)]
/// struct TooLarge([u8; 2048]);
///
@@ -94,7 +94,7 @@ declare_clippy_lint! {
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # #[derive(Clone, Copy)]
/// # struct TooLarge([u8; 2048]);
/// fn foo(v: &TooLarge) {}
@@ -208,7 +208,10 @@ impl<'tcx> PassByRefOrValue {
cx,
TRIVIALLY_COPY_PASS_BY_REF,
input.span,
- &format!("this argument ({size} byte) is passed by reference, but would be more efficient if passed by value (limit: {} byte)", self.ref_min_size),
+ &format!(
+ "this argument ({size} byte) is passed by reference, but would be more efficient if passed by value (limit: {} byte)",
+ self.ref_min_size
+ ),
"consider passing by value instead",
value_type,
Applicability::Unspecified,
diff --git a/src/tools/clippy/clippy_lints/src/pattern_type_mismatch.rs b/src/tools/clippy/clippy_lints/src/pattern_type_mismatch.rs
index 9f98195d3..dcd1e7af0 100644
--- a/src/tools/clippy/clippy_lints/src/pattern_type_mismatch.rs
+++ b/src/tools/clippy/clippy_lints/src/pattern_type_mismatch.rs
@@ -5,7 +5,7 @@ use rustc_middle::lint::in_external_macro;
use rustc_middle::ty;
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::def_id::LocalDefId;
-use rustc_span::source_map::Span;
+use rustc_span::Span;
declare_clippy_lint! {
/// ### What it does
diff --git a/src/tools/clippy/clippy_lints/src/permissions_set_readonly_false.rs b/src/tools/clippy/clippy_lints/src/permissions_set_readonly_false.rs
index 664d44d65..b98005d59 100644
--- a/src/tools/clippy/clippy_lints/src/permissions_set_readonly_false.rs
+++ b/src/tools/clippy/clippy_lints/src/permissions_set_readonly_false.rs
@@ -1,10 +1,10 @@
use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::paths;
-use clippy_utils::ty::match_type;
+use clippy_utils::ty::is_type_diagnostic_item;
use rustc_ast::ast::LitKind;
use rustc_hir::{Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::sym;
declare_clippy_lint! {
/// ### What it does
@@ -14,7 +14,7 @@ declare_clippy_lint! {
/// On Unix platforms this results in the file being world writable,
/// equivalent to `chmod a+w <file>`.
/// ### Example
- /// ```rust
+ /// ```no_run
/// use std::fs::File;
/// let f = File::create("foo.txt").unwrap();
/// let metadata = f.metadata().unwrap();
@@ -31,7 +31,7 @@ declare_lint_pass!(PermissionsSetReadonlyFalse => [PERMISSIONS_SET_READONLY_FALS
impl<'tcx> LateLintPass<'tcx> for PermissionsSetReadonlyFalse {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
if let ExprKind::MethodCall(path, receiver, [arg], _) = &expr.kind
- && match_type(cx, cx.typeck_results().expr_ty(receiver), &paths::PERMISSIONS)
+ && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(receiver), sym::FsPermissions)
&& path.ident.name == sym!(set_readonly)
&& let ExprKind::Lit(lit) = &arg.kind
&& LitKind::Bool(false) == lit.node
@@ -43,9 +43,11 @@ impl<'tcx> LateLintPass<'tcx> for PermissionsSetReadonlyFalse {
"call to `set_readonly` with argument `false`",
|diag| {
diag.note("on Unix platforms this results in the file being world writable");
- diag.help("you can set the desired permissions using `PermissionsExt`. For more information, see\n\
- https://doc.rust-lang.org/std/os/unix/fs/trait.PermissionsExt.html");
- }
+ diag.help(
+ "you can set the desired permissions using `PermissionsExt`. For more information, see\n\
+ https://doc.rust-lang.org/std/os/unix/fs/trait.PermissionsExt.html",
+ );
+ },
);
}
}
diff --git a/src/tools/clippy/clippy_lints/src/ptr.rs b/src/tools/clippy/clippy_lints/src/ptr.rs
index 7dabdcd58..83863b92c 100644
--- a/src/tools/clippy/clippy_lints/src/ptr.rs
+++ b/src/tools/clippy/clippy_lints/src/ptr.rs
@@ -4,9 +4,8 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_the
use clippy_utils::source::snippet_opt;
use clippy_utils::ty::expr_sig;
use clippy_utils::visitors::contains_unsafe_block;
-use clippy_utils::{get_expr_use_or_unification_node, is_lint_allowed, path_def_id, path_to_local, paths};
+use clippy_utils::{get_expr_use_or_unification_node, is_lint_allowed, path_def_id, path_to_local};
use hir::LifetimeName;
-use if_chain::if_chain;
use rustc_errors::{Applicability, MultiSpan};
use rustc_hir::def_id::DefId;
use rustc_hir::hir_id::HirIdMap;
@@ -22,8 +21,7 @@ use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::hir::nested_filter;
use rustc_middle::ty::{self, Binder, ClauseKind, ExistentialPredicate, List, PredicateKind, Ty};
use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::source_map::Span;
-use rustc_span::sym;
+use rustc_span::{sym, Span};
use rustc_span::symbol::Symbol;
use rustc_target::spec::abi::Abi;
use rustc_trait_selection::infer::InferCtxtExt as _;
@@ -271,60 +269,40 @@ impl<'tcx> LateLintPass<'tcx> for Ptr {
}
fn check_invalid_ptr_usage<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
- // (fn_path, arg_indices) - `arg_indices` are the `arg` positions where null would cause U.B.
- const INVALID_NULL_PTR_USAGE_TABLE: [(&[&str], &[usize]); 13] = [
- (&paths::SLICE_FROM_RAW_PARTS, &[0]),
- (&paths::SLICE_FROM_RAW_PARTS_MUT, &[0]),
- (&paths::PTR_COPY, &[0, 1]),
- (&paths::PTR_COPY_NONOVERLAPPING, &[0, 1]),
- (&paths::PTR_READ, &[0]),
- (&paths::PTR_READ_UNALIGNED, &[0]),
- (&paths::PTR_READ_VOLATILE, &[0]),
- (&paths::PTR_REPLACE, &[0]),
- (&paths::PTR_SLICE_FROM_RAW_PARTS, &[0]),
- (&paths::PTR_SLICE_FROM_RAW_PARTS_MUT, &[0]),
- (&paths::PTR_SWAP, &[0, 1]),
- (&paths::PTR_SWAP_NONOVERLAPPING, &[0, 1]),
- (&paths::PTR_WRITE_BYTES, &[0]),
- ];
- let invalid_null_ptr_usage_table_diag_items: [(Option<DefId>, &[usize]); 3] = [
- (cx.tcx.get_diagnostic_item(sym::ptr_write), &[0]),
- (cx.tcx.get_diagnostic_item(sym::ptr_write_unaligned), &[0]),
- (cx.tcx.get_diagnostic_item(sym::ptr_write_volatile), &[0]),
- ];
-
- if_chain! {
- if let ExprKind::Call(fun, args) = expr.kind;
- if let ExprKind::Path(ref qpath) = fun.kind;
- if let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id();
- let fun_def_path = cx.get_def_path(fun_def_id).into_iter().map(Symbol::to_ident_string).collect::<Vec<_>>();
- if let Some(arg_indices) = INVALID_NULL_PTR_USAGE_TABLE
- .iter()
- .find_map(|&(fn_path, indices)| if fn_path == fun_def_path { Some(indices) } else { None })
- .or_else(|| {
- invalid_null_ptr_usage_table_diag_items
- .iter()
- .find_map(|&(def_id, indices)| {
- if def_id == Some(fun_def_id) {
- Some(indices)
- } else {
- None
- }
- })
- });
- then {
- for &arg_idx in arg_indices {
- if let Some(arg) = args.get(arg_idx).filter(|arg| is_null_path(cx, arg)) {
- span_lint_and_sugg(
- cx,
- INVALID_NULL_PTR_USAGE,
- arg.span,
- "pointer must be non-null",
- "change this to",
- "core::ptr::NonNull::dangling().as_ptr()".to_string(),
- Applicability::MachineApplicable,
- );
- }
+ if let ExprKind::Call(fun, args) = expr.kind
+ && let ExprKind::Path(ref qpath) = fun.kind
+ && let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id()
+ && let Some(name) = cx.tcx.get_diagnostic_name(fun_def_id)
+ {
+ // `arg` positions where null would cause U.B.
+ let arg_indices: &[_] = match name {
+ sym::ptr_read
+ | sym::ptr_read_unaligned
+ | sym::ptr_read_volatile
+ | sym::ptr_replace
+ | sym::ptr_slice_from_raw_parts
+ | sym::ptr_slice_from_raw_parts_mut
+ | sym::ptr_write
+ | sym::ptr_write_bytes
+ | sym::ptr_write_unaligned
+ | sym::ptr_write_volatile
+ | sym::slice_from_raw_parts
+ | sym::slice_from_raw_parts_mut => &[0],
+ sym::ptr_copy | sym::ptr_copy_nonoverlapping | sym::ptr_swap | sym::ptr_swap_nonoverlapping => &[0, 1],
+ _ => return,
+ };
+
+ for &arg_idx in arg_indices {
+ if let Some(arg) = args.get(arg_idx).filter(|arg| is_null_path(cx, arg)) {
+ span_lint_and_sugg(
+ cx,
+ INVALID_NULL_PTR_USAGE,
+ arg.span,
+ "pointer must be non-null",
+ "change this to",
+ "core::ptr::NonNull::dangling().as_ptr()".to_string(),
+ Applicability::MachineApplicable,
+ );
}
}
}
@@ -434,7 +412,6 @@ impl<'tcx> DerefTy<'tcx> {
}
}
-#[expect(clippy::too_many_lines)]
fn check_fn_args<'cx, 'tcx: 'cx>(
cx: &'cx LateContext<'tcx>,
fn_sig: ty::FnSig<'tcx>,
@@ -456,104 +433,93 @@ fn check_fn_args<'cx, 'tcx: 'cx>(
&& let [.., name] = path.segments
&& cx.tcx.item_name(adt.did()) == name.ident.name
{
- let emission_id = params.get(i).map_or(hir_ty.hir_id, |param| param.hir_id);
- let (method_renames, deref_ty) = match cx.tcx.get_diagnostic_name(adt.did()) {
- Some(sym::Vec) => (
- [("clone", ".to_owned()")].as_slice(),
- DerefTy::Slice(
- name.args
- .and_then(|args| args.args.first())
- .and_then(|arg| if let GenericArg::Type(ty) = arg {
- Some(ty.span)
- } else {
- None
- }),
- args.type_at(0),
- ),
- ),
- _ if Some(adt.did()) == cx.tcx.lang_items().string() => (
- [("clone", ".to_owned()"), ("as_str", "")].as_slice(),
- DerefTy::Str,
- ),
- Some(sym::PathBuf) => (
- [("clone", ".to_path_buf()"), ("as_path", "")].as_slice(),
- DerefTy::Path,
- ),
- Some(sym::Cow) if mutability == Mutability::Not => {
- if let Some((lifetime, ty)) = name.args
- .and_then(|args| {
- if let [GenericArg::Lifetime(lifetime), ty] = args.args {
- return Some((lifetime, ty));
- }
+ let emission_id = params.get(i).map_or(hir_ty.hir_id, |param| param.hir_id);
+ let (method_renames, deref_ty) = match cx.tcx.get_diagnostic_name(adt.did()) {
+ Some(sym::Vec) => (
+ [("clone", ".to_owned()")].as_slice(),
+ DerefTy::Slice(
+ name.args.and_then(|args| args.args.first()).and_then(|arg| {
+ if let GenericArg::Type(ty) = arg {
+ Some(ty.span)
+ } else {
None
- })
- {
- if !lifetime.is_anonymous()
- && fn_sig.output()
- .walk()
- .filter_map(|arg| {
- arg.as_region().and_then(|lifetime| {
- match lifetime.kind() {
- ty::ReEarlyBound(r) => Some(r.def_id),
- ty::ReLateBound(_, r) => r.kind.get_id(),
- ty::ReFree(r) => r.bound_region.get_id(),
- ty::ReStatic
- | ty::ReVar(_)
- | ty::RePlaceholder(_)
- | ty::ReErased
- | ty::ReError(_) => None,
- }
- })
- })
- .any(|def_id| {
- matches!(
- lifetime.res,
- LifetimeName::Param(param_def_id) if def_id
- .as_local()
- .is_some_and(|def_id| def_id == param_def_id),
- )
- })
- {
- // `&Cow<'a, T>` when the return type uses 'a is okay
- return None;
}
-
- let ty_name =
- snippet_opt(cx, ty.span()).unwrap_or_else(|| args.type_at(1).to_string());
-
- span_lint_hir_and_then(
- cx,
- PTR_ARG,
- emission_id,
- hir_ty.span,
- "using a reference to `Cow` is not recommended",
- |diag| {
- diag.span_suggestion(
- hir_ty.span,
- "change this to",
- format!("&{}{ty_name}", mutability.prefix_str()),
- Applicability::Unspecified,
- );
- }
- );
+ }),
+ args.type_at(0),
+ ),
+ ),
+ _ if Some(adt.did()) == cx.tcx.lang_items().string() => {
+ ([("clone", ".to_owned()"), ("as_str", "")].as_slice(), DerefTy::Str)
+ },
+ Some(sym::PathBuf) => ([("clone", ".to_path_buf()"), ("as_path", "")].as_slice(), DerefTy::Path),
+ Some(sym::Cow) if mutability == Mutability::Not => {
+ if let Some((lifetime, ty)) = name.args.and_then(|args| {
+ if let [GenericArg::Lifetime(lifetime), ty] = args.args {
+ return Some((lifetime, ty));
}
- return None;
- },
- _ => return None,
- };
- return Some(PtrArg {
- idx: i,
- emission_id,
- span: hir_ty.span,
- ty_did: adt.did(),
- ty_name: name.ident.name,
- method_renames,
- ref_prefix: RefPrefix {
- lt: *lt,
- mutability,
- },
- deref_ty,
- });
+ None
+ }) {
+ if !lifetime.is_anonymous()
+ && fn_sig
+ .output()
+ .walk()
+ .filter_map(|arg| {
+ arg.as_region().and_then(|lifetime| match lifetime.kind() {
+ ty::ReEarlyBound(r) => Some(r.def_id),
+ ty::ReLateBound(_, r) => r.kind.get_id(),
+ ty::ReFree(r) => r.bound_region.get_id(),
+ ty::ReStatic
+ | ty::ReVar(_)
+ | ty::RePlaceholder(_)
+ | ty::ReErased
+ | ty::ReError(_) => None,
+ })
+ })
+ .any(|def_id| {
+ matches!(
+ lifetime.res,
+ LifetimeName::Param(param_def_id) if def_id
+ .as_local()
+ .is_some_and(|def_id| def_id == param_def_id),
+ )
+ })
+ {
+ // `&Cow<'a, T>` when the return type uses 'a is okay
+ return None;
+ }
+
+ let ty_name = snippet_opt(cx, ty.span()).unwrap_or_else(|| args.type_at(1).to_string());
+
+ span_lint_hir_and_then(
+ cx,
+ PTR_ARG,
+ emission_id,
+ hir_ty.span,
+ "using a reference to `Cow` is not recommended",
+ |diag| {
+ diag.span_suggestion(
+ hir_ty.span,
+ "change this to",
+ format!("&{}{ty_name}", mutability.prefix_str()),
+ Applicability::Unspecified,
+ );
+ },
+ );
+ }
+ return None;
+ },
+ _ => return None,
+ };
+ return Some(PtrArg {
+ idx: i,
+ emission_id,
+ span: hir_ty.span,
+ ty_did: adt.did(),
+ ty_name: name.ident.name,
+ method_renames,
+ ref_prefix: RefPrefix { lt: *lt, mutability },
+ deref_ty,
+ });
}
None
})
diff --git a/src/tools/clippy/clippy_lints/src/ptr_offset_with_cast.rs b/src/tools/clippy/clippy_lints/src/ptr_offset_with_cast.rs
index 20e032d4b..66d869bc4 100644
--- a/src/tools/clippy/clippy_lints/src/ptr_offset_with_cast.rs
+++ b/src/tools/clippy/clippy_lints/src/ptr_offset_with_cast.rs
@@ -17,7 +17,7 @@ declare_clippy_lint! {
/// cast by using the `add` method instead.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let vec = vec![b'a', b'b', b'c'];
/// let ptr = vec.as_ptr();
/// let offset = 1_usize;
@@ -29,7 +29,7 @@ declare_clippy_lint! {
///
/// Could be written:
///
- /// ```rust
+ /// ```no_run
/// let vec = vec![b'a', b'b', b'c'];
/// let ptr = vec.as_ptr();
/// let offset = 1_usize;
diff --git a/src/tools/clippy/clippy_lints/src/pub_use.rs b/src/tools/clippy/clippy_lints/src/pub_use.rs
index 9d2b0cedb..316a72988 100644
--- a/src/tools/clippy/clippy_lints/src/pub_use.rs
+++ b/src/tools/clippy/clippy_lints/src/pub_use.rs
@@ -14,7 +14,7 @@ declare_clippy_lint! {
/// unintentional exports or to encourage placing exported items directly in public modules
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// pub mod outer {
/// mod inner {
/// pub struct Test {}
@@ -25,7 +25,7 @@ declare_clippy_lint! {
/// use outer::Test;
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// pub mod outer {
/// pub struct Test {}
/// }
@@ -41,16 +41,17 @@ declare_lint_pass!(PubUse => [PUB_USE]);
impl EarlyLintPass for PubUse {
fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
- if let ItemKind::Use(_) = item.kind &&
- let VisibilityKind::Public = item.vis.kind {
- span_lint_and_help(
- cx,
- PUB_USE,
- item.span,
- "using `pub use`",
- None,
- "move the exported item to a public module instead",
- );
- }
+ if let ItemKind::Use(_) = item.kind
+ && let VisibilityKind::Public = item.vis.kind
+ {
+ span_lint_and_help(
+ cx,
+ PUB_USE,
+ item.span,
+ "using `pub use`",
+ None,
+ "move the exported item to a public module instead",
+ );
+ }
}
}
diff --git a/src/tools/clippy/clippy_lints/src/question_mark.rs b/src/tools/clippy/clippy_lints/src/question_mark.rs
index 734ca2914..b133635e8 100644
--- a/src/tools/clippy/clippy_lints/src/question_mark.rs
+++ b/src/tools/clippy/clippy_lints/src/question_mark.rs
@@ -1,7 +1,8 @@
-use crate::manual_let_else::{MatchLintBehaviour, MANUAL_LET_ELSE};
+use crate::manual_let_else::MANUAL_LET_ELSE;
use crate::question_mark_used::QUESTION_MARK_USED;
+use clippy_config::msrvs::Msrv;
+use clippy_config::types::MatchLintBehaviour;
use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::msrvs::Msrv;
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::ty::is_type_diagnostic_item;
use clippy_utils::{
@@ -97,16 +98,23 @@ enum IfBlockType<'hir> {
}
fn check_let_some_else_return_none(cx: &LateContext<'_>, stmt: &Stmt<'_>) {
- if let StmtKind::Local(Local { pat, init: Some(init_expr), els: Some(els), .. }) = stmt.kind &&
- let Block { stmts: &[], expr: Some(els), .. } = els &&
- let Some(inner_pat) = pat_and_expr_can_be_question_mark(cx, pat, els)
+ if let StmtKind::Local(Local {
+ pat,
+ init: Some(init_expr),
+ els: Some(els),
+ ..
+ }) = stmt.kind
+ && let Block {
+ stmts: &[],
+ expr: Some(els),
+ ..
+ } = els
+ && let Some(inner_pat) = pat_and_expr_can_be_question_mark(cx, pat, els)
{
let mut applicability = Applicability::MaybeIncorrect;
let init_expr_str = snippet_with_applicability(cx, init_expr.span, "..", &mut applicability);
let receiver_str = snippet_with_applicability(cx, inner_pat.span, "..", &mut applicability);
- let sugg = format!(
- "let {receiver_str} = {init_expr_str}?;",
- );
+ let sugg = format!("let {receiver_str} = {init_expr_str}?;",);
span_lint_and_sugg(
cx,
QUESTION_MARK,
diff --git a/src/tools/clippy/clippy_lints/src/ranges.rs b/src/tools/clippy/clippy_lints/src/ranges.rs
index 3287675a8..1b3081abc 100644
--- a/src/tools/clippy/clippy_lints/src/ranges.rs
+++ b/src/tools/clippy/clippy_lints/src/ranges.rs
@@ -1,6 +1,6 @@
+use clippy_config::msrvs::{self, Msrv};
use clippy_utils::consts::{constant, Constant};
use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then};
-use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::source::{snippet, snippet_opt, snippet_with_applicability};
use clippy_utils::sugg::Sugg;
use clippy_utils::{get_parent_expr, higher, in_constant, is_integer_const, path_to_local};
@@ -11,7 +11,8 @@ use rustc_hir::{BinOpKind, Expr, ExprKind, HirId};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty;
use rustc_session::{declare_tool_lint, impl_lint_pass};
-use rustc_span::source_map::{Span, Spanned};
+use rustc_span::Span;
+use rustc_span::source_map::Spanned;
use std::cmp::Ordering;
declare_clippy_lint! {
@@ -39,7 +40,7 @@ declare_clippy_lint! {
/// ([#3307](https://github.com/rust-lang/rust-clippy/issues/3307)).
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # let x = 0;
/// # let y = 1;
/// for i in x..(y+1) {
@@ -48,7 +49,7 @@ declare_clippy_lint! {
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # let x = 0;
/// # let y = 1;
/// for i in x..=y {
@@ -77,7 +78,7 @@ declare_clippy_lint! {
/// ([#3307](https://github.com/rust-lang/rust-clippy/issues/3307)).
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # let x = 0;
/// # let y = 1;
/// for i in x..=(y-1) {
@@ -86,7 +87,7 @@ declare_clippy_lint! {
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # let x = 0;
/// # let y = 1;
/// for i in x..y {
@@ -118,7 +119,7 @@ declare_clippy_lint! {
/// }
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// fn main() {
/// (0..=10).rev().for_each(|x| println!("{}", x));
///
@@ -142,14 +143,14 @@ declare_clippy_lint! {
/// failure modes (such as fencepost errors or using `||` instead of `&&`).
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// // given
/// let x = 6;
///
/// assert!(x >= 3 && x < 8);
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
///# let x = 6;
/// assert!((3..8).contains(&x));
/// ```
diff --git a/src/tools/clippy/clippy_lints/src/raw_strings.rs b/src/tools/clippy/clippy_lints/src/raw_strings.rs
index 8a7e48746..391c77dbf 100644
--- a/src/tools/clippy/clippy_lints/src/raw_strings.rs
+++ b/src/tools/clippy/clippy_lints/src/raw_strings.rs
@@ -20,11 +20,11 @@ declare_clippy_lint! {
/// idiomatic than a string literal, so it's opt-in.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let r = r"Hello, world!";
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// let r = "Hello, world!";
/// ```
#[clippy::version = "1.72.0"]
@@ -41,11 +41,11 @@ declare_clippy_lint! {
/// necessary.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let r = r###"Hello, "world"!"###;
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// let r = r#"Hello, "world"!"#;
/// ```
#[clippy::version = "1.72.0"]
@@ -75,6 +75,7 @@ impl EarlyLintPass for RawStrings {
if !snippet(cx, expr.span, prefix).trim().starts_with(prefix) {
return;
}
+ let descr = lit.kind.descr();
if !str.contains(['\\', '"']) {
span_lint_and_then(
@@ -89,20 +90,17 @@ impl EarlyLintPass for RawStrings {
let r_pos = expr.span.lo() + BytePos::from_usize(prefix.len() - 1);
let start = start.with_lo(r_pos);
- if end.is_empty() {
- diag.span_suggestion(
- start,
- "use a string literal instead",
- format!("\"{str}\""),
- Applicability::MachineApplicable,
- );
- } else {
- diag.multipart_suggestion(
- "try",
- vec![(start, String::new()), (end, String::new())],
- Applicability::MachineApplicable,
- );
+ let mut remove = vec![(start, String::new())];
+ // avoid debug ICE from empty suggestions
+ if !end.is_empty() {
+ remove.push((end, String::new()));
}
+
+ diag.multipart_suggestion_verbose(
+ format!("use a plain {descr} literal instead"),
+ remove,
+ Applicability::MachineApplicable,
+ );
},
);
if !matches!(cx.get_lint_level(NEEDLESS_RAW_STRINGS), rustc_lint::Allow) {
@@ -149,9 +147,9 @@ impl EarlyLintPass for RawStrings {
let (start, end) = hash_spans(expr.span, prefix, req, max);
let message = match max - req {
- _ if req == 0 => "remove all the hashes around the literal".to_string(),
- 1 => "remove one hash from both sides of the literal".to_string(),
- n => format!("remove {n} hashes from both sides of the literal"),
+ _ if req == 0 => format!("remove all the hashes around the {descr} literal"),
+ 1 => format!("remove one hash from both sides of the {descr} literal"),
+ n => format!("remove {n} hashes from both sides of the {descr} literal"),
};
diag.multipart_suggestion(
diff --git a/src/tools/clippy/clippy_lints/src/rc_clone_in_vec_init.rs b/src/tools/clippy/clippy_lints/src/rc_clone_in_vec_init.rs
index 8e85c55e7..59ce289e7 100644
--- a/src/tools/clippy/clippy_lints/src/rc_clone_in_vec_init.rs
+++ b/src/tools/clippy/clippy_lints/src/rc_clone_in_vec_init.rs
@@ -1,12 +1,12 @@
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::higher::VecArgs;
+use clippy_utils::last_path_segment;
use clippy_utils::macros::root_macro_call_first_node;
use clippy_utils::source::{indent_of, snippet};
-use clippy_utils::ty::match_type;
-use clippy_utils::{last_path_segment, paths};
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, QPath, TyKind};
use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::ty;
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::{sym, Span, Symbol};
@@ -21,13 +21,13 @@ declare_clippy_lint! {
/// than different instances.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let v = vec![std::sync::Arc::new("some data".to_string()); 100];
/// // or
/// let v = vec![std::rc::Rc::new("some data".to_string()); 100];
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// // Initialize each value separately:
/// let mut data = Vec::with_capacity(100);
/// for _ in 0..100 {
@@ -133,8 +133,9 @@ fn ref_init(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<(Symbol, Span)> {
return Some((symbol, func.span));
}
- let ty_path = cx.typeck_results().expr_ty(expr);
- if match_type(cx, ty_path, &paths::WEAK_RC) || match_type(cx, ty_path, &paths::WEAK_ARC) {
+ if let ty::Adt(adt, _) = *cx.typeck_results().expr_ty(expr).kind()
+ && matches!(cx.tcx.get_diagnostic_name(adt.did()), Some(sym::RcWeak | sym::ArcWeak))
+ {
return Some((Symbol::intern("Weak"), func.span));
}
}
diff --git a/src/tools/clippy/clippy_lints/src/read_zero_byte_vec.rs b/src/tools/clippy/clippy_lints/src/read_zero_byte_vec.rs
index 2bf90815c..b27d4cc6e 100644
--- a/src/tools/clippy/clippy_lints/src/read_zero_byte_vec.rs
+++ b/src/tools/clippy/clippy_lints/src/read_zero_byte_vec.rs
@@ -24,7 +24,7 @@ declare_clippy_lint! {
/// a zero-byte read would allocate a `Vec` for it.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// use std::io;
/// fn foo<F: io::Read>(mut f: F) {
/// let mut data = Vec::with_capacity(100);
@@ -32,7 +32,7 @@ declare_clippy_lint! {
/// }
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// use std::io;
/// fn foo<F: io::Read>(mut f: F) {
/// let mut data = Vec::with_capacity(100);
@@ -42,7 +42,7 @@ declare_clippy_lint! {
/// ```
#[clippy::version = "1.63.0"]
pub READ_ZERO_BYTE_VEC,
- correctness,
+ nursery,
"checks for reads into a zero-length `Vec`"
}
declare_lint_pass!(ReadZeroByteVec => [READ_ZERO_BYTE_VEC]);
@@ -59,7 +59,10 @@ impl<'tcx> LateLintPass<'tcx> for ReadZeroByteVec {
{
let visitor = |expr: &Expr<'_>| {
if let ExprKind::MethodCall(path, _, [arg], _) = expr.kind
- && let PathSegment { ident: read_or_read_exact, .. } = *path
+ && let PathSegment {
+ ident: read_or_read_exact,
+ ..
+ } = *path
&& matches!(read_or_read_exact.as_str(), "read" | "read_exact")
&& let ExprKind::AddrOf(_, hir::Mutability::Mut, inner) = arg.kind
&& let ExprKind::Path(QPath::Resolved(None, inner_path)) = inner.kind
@@ -72,15 +75,14 @@ impl<'tcx> LateLintPass<'tcx> for ReadZeroByteVec {
}
};
- let (read_found, next_stmt_span) =
- if let Some(next_stmt) = block.stmts.get(idx + 1) {
+ let (read_found, next_stmt_span) = if let Some(next_stmt) = block.stmts.get(idx + 1) {
// case { .. stmt; stmt; .. }
(for_each_expr(next_stmt, visitor).is_some(), next_stmt.span)
} else if let Some(e) = block.expr {
// case { .. stmt; expr }
(for_each_expr(e, visitor).is_some(), e.span)
} else {
- return
+ return;
};
if read_found && !next_stmt_span.from_expansion() {
@@ -93,13 +95,14 @@ impl<'tcx> LateLintPass<'tcx> for ReadZeroByteVec {
next_stmt_span,
"reading zero byte data to `Vec`",
"try",
- format!("{}.resize({len}, 0); {}",
+ format!(
+ "{}.resize({len}, 0); {}",
ident.as_str(),
snippet(cx, next_stmt_span, "..")
),
applicability,
);
- }
+ },
VecInitKind::WithExprCapacity(hir_id) => {
let e = cx.tcx.hir().expect_expr(hir_id);
span_lint_and_sugg(
@@ -108,14 +111,15 @@ impl<'tcx> LateLintPass<'tcx> for ReadZeroByteVec {
next_stmt_span,
"reading zero byte data to `Vec`",
"try",
- format!("{}.resize({}, 0); {}",
+ format!(
+ "{}.resize({}, 0); {}",
ident.as_str(),
snippet(cx, e.span, ".."),
snippet(cx, next_stmt_span, "..")
),
applicability,
);
- }
+ },
_ => {
span_lint(
cx,
@@ -123,8 +127,7 @@ impl<'tcx> LateLintPass<'tcx> for ReadZeroByteVec {
next_stmt_span,
"reading zero byte data to `Vec`",
);
-
- }
+ },
}
}
}
diff --git a/src/tools/clippy/clippy_lints/src/redundant_async_block.rs b/src/tools/clippy/clippy_lints/src/redundant_async_block.rs
index 534b2762b..90297ca8b 100644
--- a/src/tools/clippy/clippy_lints/src/redundant_async_block.rs
+++ b/src/tools/clippy/clippy_lints/src/redundant_async_block.rs
@@ -5,7 +5,7 @@ use clippy_utils::peel_blocks;
use clippy_utils::source::{snippet, walk_span_to_context};
use clippy_utils::visitors::for_each_expr;
use rustc_errors::Applicability;
-use rustc_hir::{AsyncGeneratorKind, Closure, Expr, ExprKind, GeneratorKind, MatchSource};
+use rustc_hir::{Closure, CoroutineKind, CoroutineSource, Expr, ExprKind, MatchSource};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::lint::in_external_macro;
use rustc_middle::ty::UpvarCapture;
@@ -19,7 +19,7 @@ declare_clippy_lint! {
/// It is simpler and more efficient to use the future directly.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let f = async {
/// 1 + 2
/// };
@@ -28,7 +28,7 @@ declare_clippy_lint! {
/// };
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// let f = async {
/// 1 + 2
/// };
@@ -48,7 +48,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantAsyncBlock {
let Some(body_expr) = desugar_async_block(cx, expr) &&
let Some(expr) = desugar_await(peel_blocks(body_expr)) &&
// The await prefix must not come from a macro as its content could change in the future.
- expr.span.ctxt() == body_expr.span.ctxt() &&
+ expr.span.eq_ctxt(body_expr.span) &&
// An async block does not have immediate side-effects from a `.await` point-of-view.
(!expr.can_have_side_effects() || desugar_async_block(cx, expr).is_some()) &&
let Some(shortened_span) = walk_span_to_context(expr.span, span.ctxt())
@@ -69,12 +69,11 @@ impl<'tcx> LateLintPass<'tcx> for RedundantAsyncBlock {
/// If `expr` is a desugared `async` block, return the original expression if it does not capture
/// any variable by ref.
fn desugar_async_block<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
- if let ExprKind::Closure(Closure { body, def_id, .. }) = expr.kind &&
- let body = cx.tcx.hir().body(*body) &&
- matches!(body.generator_kind, Some(GeneratorKind::Async(AsyncGeneratorKind::Block)))
+ if let ExprKind::Closure(Closure { body, def_id, .. }) = expr.kind
+ && let body = cx.tcx.hir().body(*body)
+ && matches!(body.coroutine_kind, Some(CoroutineKind::Async(CoroutineSource::Block)))
{
- cx
- .typeck_results()
+ cx.typeck_results()
.closure_min_captures
.get(def_id)
.map_or(true, |m| {
@@ -93,12 +92,13 @@ fn desugar_async_block<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Op
/// If `expr` is a desugared `.await`, return the original expression if it does not come from a
/// macro expansion.
fn desugar_await<'tcx>(expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
- if let ExprKind::Match(match_value, _, MatchSource::AwaitDesugar) = expr.kind &&
- let ExprKind::Call(_, [into_future_arg]) = match_value.kind &&
- let ctxt = expr.span.ctxt() &&
- for_each_expr(into_future_arg, |e|
- walk_span_to_context(e.span, ctxt)
- .map_or(ControlFlow::Break(()), |_| ControlFlow::Continue(()))).is_none()
+ if let ExprKind::Match(match_value, _, MatchSource::AwaitDesugar) = expr.kind
+ && let ExprKind::Call(_, [into_future_arg]) = match_value.kind
+ && let ctxt = expr.span.ctxt()
+ && for_each_expr(into_future_arg, |e| {
+ walk_span_to_context(e.span, ctxt).map_or(ControlFlow::Break(()), |_| ControlFlow::Continue(()))
+ })
+ .is_none()
{
Some(into_future_arg)
} else {
diff --git a/src/tools/clippy/clippy_lints/src/redundant_clone.rs b/src/tools/clippy/clippy_lints/src/redundant_clone.rs
index e36adef55..8daf085a4 100644
--- a/src/tools/clippy/clippy_lints/src/redundant_clone.rs
+++ b/src/tools/clippy/clippy_lints/src/redundant_clone.rs
@@ -12,8 +12,7 @@ use rustc_middle::mir;
use rustc_middle::ty::{self, Ty};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::def_id::LocalDefId;
-use rustc_span::source_map::{BytePos, Span};
-use rustc_span::sym;
+use rustc_span::{sym, BytePos, Span};
macro_rules! unwrap_or_continue {
($x:expr) => {
@@ -37,7 +36,7 @@ declare_clippy_lint! {
/// False-negatives: analysis performed by this lint is conservative and limited.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # use std::path::Path;
/// # #[derive(Clone)]
/// # struct Foo;
@@ -99,8 +98,8 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone {
unwrap_or_continue!(is_call_with_ref_arg(cx, mir, &terminator.kind));
let from_borrow = match_def_path(cx, fn_def_id, &paths::CLONE_TRAIT_METHOD)
- || match_def_path(cx, fn_def_id, &paths::TO_OWNED_METHOD)
- || (match_def_path(cx, fn_def_id, &paths::TO_STRING_METHOD)
+ || cx.tcx.is_diagnostic_item(sym::to_owned_method, fn_def_id)
+ || (cx.tcx.is_diagnostic_item(sym::to_string_method, fn_def_id)
&& is_type_lang_item(cx, arg_ty, LangItem::String));
let from_deref = !from_borrow
diff --git a/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs b/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs
index f42836611..e679fab53 100644
--- a/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs
+++ b/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs
@@ -23,12 +23,12 @@ declare_clippy_lint! {
/// complexity.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let a = (|| 42)();
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// let a = 42;
/// ```
#[clippy::version = "pre 1.29.0"]
@@ -98,11 +98,15 @@ fn find_innermost_closure<'tcx>(
&& steps > 0
{
expr = body.value;
- data = Some((body.value, closure.fn_decl, if is_async_closure(body) {
- ty::Asyncness::Yes
- } else {
- ty::Asyncness::No
- }));
+ data = Some((
+ body.value,
+ closure.fn_decl,
+ if is_async_closure(body) {
+ ty::Asyncness::Yes
+ } else {
+ ty::Asyncness::No
+ },
+ ));
steps -= 1;
}
@@ -144,7 +148,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClosureCall {
// without this check, we'd end up linting twice.
&& !matches!(recv.kind, hir::ExprKind::Call(..))
&& let (full_expr, call_depth) = get_parent_call_exprs(cx, expr)
- && let Some((body, fn_decl, generator_kind)) = find_innermost_closure(cx, recv, call_depth)
+ && let Some((body, fn_decl, coroutine_kind)) = find_innermost_closure(cx, recv, call_depth)
{
span_lint_and_then(
cx,
@@ -154,9 +158,10 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClosureCall {
|diag| {
if fn_decl.inputs.is_empty() {
let mut applicability = Applicability::MachineApplicable;
- let mut hint = Sugg::hir_with_context(cx, body, full_expr.span.ctxt(), "..", &mut applicability);
+ let mut hint =
+ Sugg::hir_with_context(cx, body, full_expr.span.ctxt(), "..", &mut applicability);
- if generator_kind.is_async()
+ if coroutine_kind.is_async()
&& let hir::ExprKind::Closure(closure) = body.kind
{
let async_closure_body = cx.tcx.hir().body(closure.body);
@@ -173,10 +178,10 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClosureCall {
full_expr.span,
"try doing something like",
hint.maybe_par(),
- applicability
+ applicability,
);
}
- }
+ },
);
}
}
diff --git a/src/tools/clippy/clippy_lints/src/redundant_else.rs b/src/tools/clippy/clippy_lints/src/redundant_else.rs
index 73088ce1a..221aa317e 100644
--- a/src/tools/clippy/clippy_lints/src/redundant_else.rs
+++ b/src/tools/clippy/clippy_lints/src/redundant_else.rs
@@ -16,7 +16,7 @@ declare_clippy_lint! {
/// Some may prefer to keep the `else` block for clarity.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// fn my_func(count: u32) {
/// if count == 0 {
/// print!("Nothing to do");
@@ -27,7 +27,7 @@ declare_clippy_lint! {
/// }
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// fn my_func(count: u32) {
/// if count == 0 {
/// print!("Nothing to do");
diff --git a/src/tools/clippy/clippy_lints/src/redundant_field_names.rs b/src/tools/clippy/clippy_lints/src/redundant_field_names.rs
index 61bff4a0e..b8e606df7 100644
--- a/src/tools/clippy/clippy_lints/src/redundant_field_names.rs
+++ b/src/tools/clippy/clippy_lints/src/redundant_field_names.rs
@@ -1,5 +1,5 @@
+use clippy_config::msrvs::{self, Msrv};
use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::msrvs::{self, Msrv};
use rustc_ast::ast::{Expr, ExprKind};
use rustc_errors::Applicability;
use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
@@ -16,7 +16,7 @@ declare_clippy_lint! {
/// the field name is redundant.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let bar: u8 = 123;
///
/// struct Foo {
diff --git a/src/tools/clippy/clippy_lints/src/redundant_locals.rs b/src/tools/clippy/clippy_lints/src/redundant_locals.rs
index 0c89c7ee4..6bc0d0618 100644
--- a/src/tools/clippy/clippy_lints/src/redundant_locals.rs
+++ b/src/tools/clippy/clippy_lints/src/redundant_locals.rs
@@ -3,11 +3,9 @@ use clippy_utils::is_from_proc_macro;
use clippy_utils::ty::needs_ordered_drop;
use rustc_ast::Mutability;
use rustc_hir::def::Res;
-use rustc_hir::{
- BindingAnnotation, ByRef, Expr, ExprKind, HirId, Local, Node, Pat, PatKind, QPath,
-};
+use rustc_hir::{BindingAnnotation, ByRef, ExprKind, HirId, Local, Node, Pat, PatKind, QPath};
use rustc_lint::{LateContext, LateLintPass, LintContext};
-use rustc_middle::lint::{in_external_macro, is_from_async_await};
+use rustc_middle::lint::in_external_macro;
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::symbol::Ident;
use rustc_span::DesugaringKind;
@@ -22,7 +20,7 @@ declare_clippy_lint! {
/// Note that although these bindings do not affect your code's meaning, they _may_ affect `rustc`'s stack allocation.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let a = 0;
/// let a = a;
///
@@ -31,7 +29,7 @@ declare_clippy_lint! {
/// }
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// let a = 0;
/// // no redefinition with the same name
///
@@ -39,7 +37,7 @@ declare_clippy_lint! {
/// // no redefinition with the same name
/// }
/// ```
- #[clippy::version = "1.72.0"]
+ #[clippy::version = "1.73.0"]
pub REDUNDANT_LOCALS,
correctness,
"redundant redefinition of a local binding"
@@ -64,25 +62,22 @@ impl<'tcx> LateLintPass<'tcx> for RedundantLocals {
if let Res::Local(binding_id) = cx.qpath_res(&qpath, expr.hir_id);
if let Node::Pat(binding_pat) = cx.tcx.hir().get(binding_id);
// the previous binding has the same mutability
- if find_binding(binding_pat, ident).unwrap().1 == mutability;
+ if find_binding(binding_pat, ident).is_some_and(|bind| bind.1 == mutability);
// the local does not change the effect of assignments to the binding. see #11290
if !affects_assignments(cx, mutability, binding_id, local.hir_id);
// the local does not affect the code's drop behavior
- if !affects_drop_behavior(cx, binding_id, local.hir_id, expr);
+ if !needs_ordered_drop(cx, cx.typeck_results().expr_ty(expr));
// the local is user-controlled
if !in_external_macro(cx.sess(), local.span);
if !is_from_proc_macro(cx, expr);
- // Async function parameters are lowered into the closure body, so we can't lint them.
- // see `lower_maybe_async_body` in `rust_ast_lowering`
- if !is_from_async_await(local.span);
then {
span_lint_and_help(
cx,
REDUNDANT_LOCALS,
- vec![binding_pat.span, local.span],
- "redundant redefinition of a binding",
- None,
- &format!("remove the redefinition of `{ident}`"),
+ local.span,
+ &format!("redundant redefinition of a binding `{ident}`"),
+ Some(binding_pat.span),
+ &format!("`{ident}` is initially defined here"),
);
}
}
@@ -109,18 +104,3 @@ fn affects_assignments(cx: &LateContext<'_>, mutability: Mutability, bind: HirId
// the binding is mutable and the rebinding is in a different scope than the original binding
mutability == Mutability::Mut && hir.get_enclosing_scope(bind) != hir.get_enclosing_scope(rebind)
}
-
-/// Check if a rebinding of a local affects the code's drop behavior.
-fn affects_drop_behavior<'tcx>(
- cx: &LateContext<'tcx>,
- bind: HirId,
- rebind: HirId,
- rebind_expr: &Expr<'tcx>,
-) -> bool {
- let hir = cx.tcx.hir();
-
- // the rebinding is in a different scope than the original binding
- // and the type of the binding cares about drop order
- hir.get_enclosing_scope(bind) != hir.get_enclosing_scope(rebind)
- && needs_ordered_drop(cx, cx.typeck_results().expr_ty(rebind_expr))
-}
diff --git a/src/tools/clippy/clippy_lints/src/redundant_pub_crate.rs b/src/tools/clippy/clippy_lints/src/redundant_pub_crate.rs
index c2a8db7df..03673eb27 100644
--- a/src/tools/clippy/clippy_lints/src/redundant_pub_crate.rs
+++ b/src/tools/clippy/clippy_lints/src/redundant_pub_crate.rs
@@ -18,14 +18,14 @@ declare_clippy_lint! {
/// module's visibility.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// mod internal {
/// pub(crate) fn internal_fn() { }
/// }
/// ```
/// This function is not visible outside the module and it can be declared with `pub` or
/// private visibility
- /// ```rust
+ /// ```no_run
/// mod internal {
/// pub fn internal_fn() { }
/// }
diff --git a/src/tools/clippy/clippy_lints/src/redundant_slicing.rs b/src/tools/clippy/clippy_lints/src/redundant_slicing.rs
index 4abfa0fc3..7adbd6791 100644
--- a/src/tools/clippy/clippy_lints/src/redundant_slicing.rs
+++ b/src/tools/clippy/clippy_lints/src/redundant_slicing.rs
@@ -51,12 +51,12 @@ declare_clippy_lint! {
/// Some people may prefer to dereference rather than slice.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let vec = vec![1, 2, 3];
/// let slice = &vec[..];
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// let vec = vec![1, 2, 3];
/// let slice = &*vec;
/// ```
diff --git a/src/tools/clippy/clippy_lints/src/redundant_static_lifetimes.rs b/src/tools/clippy/clippy_lints/src/redundant_static_lifetimes.rs
index ed42a422b..a70b831a8 100644
--- a/src/tools/clippy/clippy_lints/src/redundant_static_lifetimes.rs
+++ b/src/tools/clippy/clippy_lints/src/redundant_static_lifetimes.rs
@@ -1,5 +1,5 @@
+use clippy_config::msrvs::{self, Msrv};
use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::source::snippet;
use rustc_ast::ast::{ConstItem, Item, ItemKind, StaticItem, Ty, TyKind};
use rustc_errors::Applicability;
diff --git a/src/tools/clippy/clippy_lints/src/redundant_type_annotations.rs b/src/tools/clippy/clippy_lints/src/redundant_type_annotations.rs
index 1d4fdb43a..f6af9cac3 100644
--- a/src/tools/clippy/clippy_lints/src/redundant_type_annotations.rs
+++ b/src/tools/clippy/clippy_lints/src/redundant_type_annotations.rs
@@ -24,11 +24,11 @@ declare_clippy_lint! {
/// - `Path` to anything else than a primitive type.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let foo: String = String::new();
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// let foo = String::new();
/// ```
#[clippy::version = "1.72.0"]
@@ -145,8 +145,8 @@ impl LateLintPass<'_> for RedundantTypeAnnotations {
hir::ExprKind::Call(init_call, _) => {
if let hir::TyKind::Path(ty_path) = &ty.kind
&& let hir::QPath::Resolved(_, resolved_path_ty) = ty_path
-
- && is_redundant_in_func_call(cx, resolved_path_ty.res, init_call) {
+ && is_redundant_in_func_call(cx, resolved_path_ty.res, init_call)
+ {
span_lint(cx, REDUNDANT_TYPE_ANNOTATIONS, local.span, "redundant type annotation");
}
},
@@ -164,11 +164,11 @@ impl LateLintPass<'_> for RedundantTypeAnnotations {
&& let hir::QPath::Resolved(_, resolved_path_ty) = ty_path
&& let Some(func_ty) = func_hir_id_to_func_ty(cx, init.hir_id)
&& let Some(return_type) = func_ty_to_return_type(cx, func_ty)
- && is_same_type(cx, resolved_path_ty.res, if is_ref {
- return_type.peel_refs()
- } else {
- return_type
- })
+ && is_same_type(
+ cx,
+ resolved_path_ty.res,
+ if is_ref { return_type.peel_refs() } else { return_type },
+ )
{
span_lint(cx, REDUNDANT_TYPE_ANNOTATIONS, local.span, "redundant type annotation");
}
@@ -177,10 +177,8 @@ impl LateLintPass<'_> for RedundantTypeAnnotations {
hir::ExprKind::Path(init_path) => {
// TODO: check for non primty
if let Some(primty) = extract_primty(&ty.kind)
-
&& let hir::QPath::TypeRelative(init_ty, _) = init_path
&& let Some(primty_init) = extract_primty(&init_ty.kind)
-
&& primty == primty_init
{
span_lint(cx, REDUNDANT_TYPE_ANNOTATIONS, local.span, "redundant type annotation");
@@ -205,8 +203,8 @@ impl LateLintPass<'_> for RedundantTypeAnnotations {
},
LitKind::Err => (),
}
- }
- _ => ()
+ },
+ _ => (),
}
};
}
diff --git a/src/tools/clippy/clippy_lints/src/ref_patterns.rs b/src/tools/clippy/clippy_lints/src/ref_patterns.rs
index b1530eed1..8b3dabde9 100644
--- a/src/tools/clippy/clippy_lints/src/ref_patterns.rs
+++ b/src/tools/clippy/clippy_lints/src/ref_patterns.rs
@@ -10,12 +10,12 @@ declare_clippy_lint! {
/// The `ref` keyword can be confusing for people unfamiliar with it, and often
/// it is more concise to use `&` instead.
/// ### Example
- /// ```rust
+ /// ```no_run
/// let opt = Some(5);
/// if let Some(ref foo) = opt {}
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// let opt = Some(5);
/// if let Some(foo) = &opt {}
/// ```
@@ -29,7 +29,7 @@ declare_lint_pass!(RefPatterns => [REF_PATTERNS]);
impl EarlyLintPass for RefPatterns {
fn check_pat(&mut self, cx: &EarlyContext<'_>, pat: &Pat) {
if let PatKind::Ident(BindingAnnotation::REF, _, _) = pat.kind
- && !pat.span.from_expansion()
+ && !pat.span.from_expansion()
{
span_lint_and_help(
cx,
diff --git a/src/tools/clippy/clippy_lints/src/reference.rs b/src/tools/clippy/clippy_lints/src/reference.rs
index db870ec4c..12da29f11 100644
--- a/src/tools/clippy/clippy_lints/src/reference.rs
+++ b/src/tools/clippy/clippy_lints/src/reference.rs
@@ -50,7 +50,7 @@ impl EarlyLintPass for DerefAddrOf {
if_chain! {
if let ExprKind::Unary(UnOp::Deref, ref deref_target) = e.kind;
if let ExprKind::AddrOf(_, ref mutability, ref addrof_target) = without_parens(deref_target).kind;
- if deref_target.span.ctxt() == e.span.ctxt();
+ if deref_target.span.eq_ctxt(e.span);
if !addrof_target.span.from_expansion();
then {
let mut applicability = Applicability::MachineApplicable;
diff --git a/src/tools/clippy/clippy_lints/src/regex.rs b/src/tools/clippy/clippy_lints/src/regex.rs
index b795e4b15..cb78eec9e 100644
--- a/src/tools/clippy/clippy_lints/src/regex.rs
+++ b/src/tools/clippy/clippy_lints/src/regex.rs
@@ -9,7 +9,7 @@ use rustc_hir::def_id::DefIdMap;
use rustc_hir::{BorrowKind, Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_tool_lint, impl_lint_pass};
-use rustc_span::source_map::{BytePos, Span};
+use rustc_span::{BytePos, Span};
declare_clippy_lint! {
/// ### What it does
diff --git a/src/tools/clippy/clippy_lints/src/reserve_after_initialization.rs b/src/tools/clippy/clippy_lints/src/reserve_after_initialization.rs
index 0c8c904e3..1975946c6 100644
--- a/src/tools/clippy/clippy_lints/src/reserve_after_initialization.rs
+++ b/src/tools/clippy/clippy_lints/src/reserve_after_initialization.rs
@@ -18,18 +18,18 @@ declare_clippy_lint! {
/// The `Vec::with_capacity` constructor is less complex.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let mut v: Vec<usize> = vec![];
/// v.reserve(10);
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// let mut v: Vec<usize> = Vec::with_capacity(10);
/// ```
#[clippy::version = "1.73.0"]
pub RESERVE_AFTER_INITIALIZATION,
complexity,
- "`reserve` called immediatly after `Vec` creation"
+ "`reserve` called immediately after `Vec` creation"
}
impl_lint_pass!(ReserveAfterInitialization => [RESERVE_AFTER_INITIALIZATION]);
@@ -74,15 +74,24 @@ impl<'tcx> LateLintPass<'tcx> for ReserveAfterInitialization {
&& let PatKind::Binding(BindingAnnotation::MUT, id, _, None) = local.pat.kind
&& !in_external_macro(cx.sess(), local.span)
&& let Some(init) = get_vec_init_kind(cx, init_expr)
- && !matches!(init, VecInitKind::WithExprCapacity(_) | VecInitKind::WithConstCapacity(_))
+ && !matches!(
+ init,
+ VecInitKind::WithExprCapacity(_) | VecInitKind::WithConstCapacity(_)
+ )
{
self.searcher = Some(VecReserveSearcher {
local_id: id,
err_span: local.span,
- init_part: snippet(cx, local.span.shrink_to_lo()
- .to(init_expr.span.source_callsite().shrink_to_lo()), "..")
- .into_owned(),
- space_hint: String::new()
+ init_part: snippet(
+ cx,
+ local
+ .span
+ .shrink_to_lo()
+ .to(init_expr.span.source_callsite().shrink_to_lo()),
+ "..",
+ )
+ .into_owned(),
+ space_hint: String::new(),
});
}
}
@@ -94,15 +103,21 @@ impl<'tcx> LateLintPass<'tcx> for ReserveAfterInitialization {
&& let Res::Local(id) = path.res
&& !in_external_macro(cx.sess(), expr.span)
&& let Some(init) = get_vec_init_kind(cx, right)
- && !matches!(init, VecInitKind::WithExprCapacity(_) | VecInitKind::WithConstCapacity(_))
+ && !matches!(
+ init,
+ VecInitKind::WithExprCapacity(_) | VecInitKind::WithConstCapacity(_)
+ )
{
self.searcher = Some(VecReserveSearcher {
local_id: id,
err_span: expr.span,
- init_part: snippet(cx, left.span.shrink_to_lo()
- .to(right.span.source_callsite().shrink_to_lo()), "..")
- .into_owned(), // see `assign_expression` test
- space_hint: String::new()
+ init_part: snippet(
+ cx,
+ left.span.shrink_to_lo().to(right.span.source_callsite().shrink_to_lo()),
+ "..",
+ )
+ .into_owned(), // see `assign_expression` test
+ space_hint: String::new(),
});
}
}
@@ -118,7 +133,7 @@ impl<'tcx> LateLintPass<'tcx> for ReserveAfterInitialization {
self.searcher = Some(VecReserveSearcher {
err_span: searcher.err_span.to(stmt.span),
space_hint: snippet(cx, space_hint.span, "..").into_owned(),
- .. searcher
+ ..searcher
});
} else {
searcher.display_err(cx);
diff --git a/src/tools/clippy/clippy_lints/src/return_self_not_must_use.rs b/src/tools/clippy/clippy_lints/src/return_self_not_must_use.rs
index bccf421e8..245029a06 100644
--- a/src/tools/clippy/clippy_lints/src/return_self_not_must_use.rs
+++ b/src/tools/clippy/clippy_lints/src/return_self_not_must_use.rs
@@ -26,7 +26,7 @@ declare_clippy_lint! {
/// if it was added on constructors for example.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// pub struct Bar;
/// impl Bar {
/// // Missing attribute
@@ -37,7 +37,7 @@ declare_clippy_lint! {
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # {
/// // It's better to have the `#[must_use]` attribute on the method like this:
/// pub struct Bar;
diff --git a/src/tools/clippy/clippy_lints/src/returns.rs b/src/tools/clippy/clippy_lints/src/returns.rs
index d6b9a49d2..f2a3dc509 100644
--- a/src/tools/clippy/clippy_lints/src/returns.rs
+++ b/src/tools/clippy/clippy_lints/src/returns.rs
@@ -1,5 +1,6 @@
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then, span_lint_hir_and_then};
use clippy_utils::source::{snippet_opt, snippet_with_context};
+use clippy_utils::sugg::has_enclosing_paren;
use clippy_utils::visitors::{for_each_expr_with_closures, Descend};
use clippy_utils::{fn_def_id, is_from_proc_macro, path_to_local_id, span_find_starting_semi};
use core::ops::ControlFlow;
@@ -14,8 +15,7 @@ use rustc_middle::lint::in_external_macro;
use rustc_middle::ty::{self, GenericArgKind, Ty};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::def_id::LocalDefId;
-use rustc_span::source_map::Span;
-use rustc_span::{BytePos, Pos};
+use rustc_span::{BytePos, Pos, Span};
use std::borrow::Cow;
declare_clippy_lint! {
@@ -34,14 +34,14 @@ declare_clippy_lint! {
/// bound without first assigning it to a let-binding.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// fn foo() -> String {
/// let x = String::new();
/// x
/// }
/// ```
/// instead, use
- /// ```
+ /// ```no_run
/// fn foo() -> String {
/// String::new()
/// }
@@ -61,13 +61,13 @@ declare_clippy_lint! {
/// more rusty.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// fn foo(x: usize) -> usize {
/// return x;
/// }
/// ```
/// simplify to
- /// ```rust
+ /// ```no_run
/// fn foo(x: usize) -> usize {
/// x
/// }
@@ -213,6 +213,9 @@ impl<'tcx> LateLintPass<'tcx> for Return {
if let Some(mut snippet) = snippet_opt(cx, initexpr.span) {
if !cx.typeck_results().expr_adjustments(retexpr).is_empty() {
+ if !has_enclosing_paren(&snippet) {
+ snippet = format!("({snippet})");
+ }
snippet.push_str(" as _");
}
err.multipart_suggestion(
diff --git a/src/tools/clippy/clippy_lints/src/same_name_method.rs b/src/tools/clippy/clippy_lints/src/same_name_method.rs
index a37e2772d..fd1f3d390 100644
--- a/src/tools/clippy/clippy_lints/src/same_name_method.rs
+++ b/src/tools/clippy/clippy_lints/src/same_name_method.rs
@@ -18,7 +18,7 @@ declare_clippy_lint! {
/// Confusing.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// trait T {
/// fn foo(&self) {}
/// }
@@ -120,9 +120,10 @@ impl<'tcx> LateLintPass<'tcx> for SameNameMethod {
}
};
- for impl_item_ref in (*items).iter().filter(|impl_item_ref| {
- matches!(impl_item_ref.kind, rustc_hir::AssocItemKind::Fn { .. })
- }) {
+ for impl_item_ref in (*items)
+ .iter()
+ .filter(|impl_item_ref| matches!(impl_item_ref.kind, rustc_hir::AssocItemKind::Fn { .. }))
+ {
let method_name = impl_item_ref.ident.name;
methods_in_trait.remove(&method_name);
check_trait_method(method_name, impl_item_ref.span);
@@ -133,9 +134,10 @@ impl<'tcx> LateLintPass<'tcx> for SameNameMethod {
}
},
None => {
- for impl_item_ref in (*items).iter().filter(|impl_item_ref| {
- matches!(impl_item_ref.kind, rustc_hir::AssocItemKind::Fn { .. })
- }) {
+ for impl_item_ref in (*items)
+ .iter()
+ .filter(|impl_item_ref| matches!(impl_item_ref.kind, rustc_hir::AssocItemKind::Fn { .. }))
+ {
let method_name = impl_item_ref.ident.name;
let impl_span = impl_item_ref.span;
let hir_id = impl_item_ref.id.hir_id();
diff --git a/src/tools/clippy/clippy_lints/src/semicolon_block.rs b/src/tools/clippy/clippy_lints/src/semicolon_block.rs
index 88f295c72..b0601bba4 100644
--- a/src/tools/clippy/clippy_lints/src/semicolon_block.rs
+++ b/src/tools/clippy/clippy_lints/src/semicolon_block.rs
@@ -19,13 +19,13 @@ declare_clippy_lint! {
///
/// ### Example
///
- /// ```rust
+ /// ```no_run
/// # fn f(_: u32) {}
/// # let x = 0;
/// unsafe { f(x) };
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # fn f(_: u32) {}
/// # let x = 0;
/// unsafe { f(x); }
@@ -48,13 +48,13 @@ declare_clippy_lint! {
///
/// ### Example
///
- /// ```rust
+ /// ```no_run
/// # fn f(_: u32) {}
/// # let x = 0;
/// unsafe { f(x); }
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # fn f(_: u32) {}
/// # let x = 0;
/// unsafe { f(x) };
diff --git a/src/tools/clippy/clippy_lints/src/semicolon_if_nothing_returned.rs b/src/tools/clippy/clippy_lints/src/semicolon_if_nothing_returned.rs
index c9547cd95..ccf8b9977 100644
--- a/src/tools/clippy/clippy_lints/src/semicolon_if_nothing_returned.rs
+++ b/src/tools/clippy/clippy_lints/src/semicolon_if_nothing_returned.rs
@@ -17,13 +17,13 @@ declare_clippy_lint! {
/// code, it doesn't require a change in previous last line.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// fn main() {
/// println!("Hello world")
/// }
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// fn main() {
/// println!("Hello world");
/// }
diff --git a/src/tools/clippy/clippy_lints/src/shadow.rs b/src/tools/clippy/clippy_lints/src/shadow.rs
index 78418b223..41c10b34a 100644
--- a/src/tools/clippy/clippy_lints/src/shadow.rs
+++ b/src/tools/clippy/clippy_lints/src/shadow.rs
@@ -21,13 +21,13 @@ declare_clippy_lint! {
/// lint to `Warn`.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # let x = 1;
/// let x = &x;
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # let x = 1;
/// let y = &x; // use different variable name
/// ```
@@ -49,12 +49,12 @@ declare_clippy_lint! {
/// the code.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let x = 2;
/// let x = x + 1;
/// ```
/// use different variable name:
- /// ```rust
+ /// ```no_run
/// let x = 2;
/// let y = x + 1;
/// ```
@@ -77,7 +77,7 @@ declare_clippy_lint! {
/// names to bindings or introducing more scopes to contain the bindings.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # let y = 1;
/// # let z = 2;
/// let x = y;
@@ -85,7 +85,7 @@ declare_clippy_lint! {
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # let y = 1;
/// # let z = 2;
/// let x = y;
diff --git a/src/tools/clippy/clippy_lints/src/significant_drop_tightening.rs b/src/tools/clippy/clippy_lints/src/significant_drop_tightening.rs
index 4b248c9c7..57bcee1a8 100644
--- a/src/tools/clippy/clippy_lints/src/significant_drop_tightening.rs
+++ b/src/tools/clippy/clippy_lints/src/significant_drop_tightening.rs
@@ -236,9 +236,13 @@ impl<'ap, 'lc, 'others, 'stmt, 'tcx> StmtsChecker<'ap, 'lc, 'others, 'stmt, 'tcx
fn manage_has_expensive_expr_after_last_attr(&mut self) {
let has_expensive_stmt = match self.ap.curr_stmt.kind {
hir::StmtKind::Expr(expr) if is_inexpensive_expr(expr) => false,
- hir::StmtKind::Local(local) if let Some(expr) = local.init
- && let hir::ExprKind::Path(_) = expr.kind => false,
- _ => true
+ hir::StmtKind::Local(local)
+ if let Some(expr) = local.init
+ && let hir::ExprKind::Path(_) = expr.kind =>
+ {
+ false
+ },
+ _ => true,
};
if has_expensive_stmt {
for apa in self.ap.apas.values_mut() {
@@ -292,8 +296,7 @@ impl<'ap, 'lc, 'others, 'stmt, 'tcx> Visitor<'tcx> for StmtsChecker<'ap, 'lc, 'o
&& {
if let Some(local_hir_id) = path_to_local(expr) {
local_hir_id == hir_id
- }
- else {
+ } else {
true
}
}
@@ -306,8 +309,7 @@ impl<'ap, 'lc, 'others, 'stmt, 'tcx> Visitor<'tcx> for StmtsChecker<'ap, 'lc, 'o
let expr_or_init = expr_or_init(self.cx, expr);
if let hir::ExprKind::MethodCall(_, local_expr, _, span) = expr_or_init.kind {
local_expr.span.to(span)
- }
- else {
+ } else {
expr_or_init.span
}
},
@@ -317,8 +319,12 @@ impl<'ap, 'lc, 'others, 'stmt, 'tcx> Visitor<'tcx> for StmtsChecker<'ap, 'lc, 'o
modify_apa_params(&mut apa);
let _ = self.ap.apas.insert(hir_id, apa);
} else {
- let Some(hir_id) = path_to_local(expr) else { return; };
- let Some(apa) = self.ap.apas.get_mut(&hir_id) else { return; };
+ let Some(hir_id) = path_to_local(expr) else {
+ return;
+ };
+ let Some(apa) = self.ap.apas.get_mut(&hir_id) else {
+ return;
+ };
match self.ap.curr_stmt.kind {
hir::StmtKind::Local(local) => {
if let hir::PatKind::Binding(_, _, ident, _) = local.pat.kind {
@@ -437,19 +443,20 @@ fn has_drop(expr: &hir::Expr<'_>, first_bind_ident: &Ident, lcx: &LateContext<'_
{
let has_ident = |local_expr: &hir::Expr<'_>| {
if let hir::ExprKind::Path(hir::QPath::Resolved(_, arg_path)) = &local_expr.kind
- && let [first_arg_ps, .. ] = arg_path.segments
+ && let [first_arg_ps, ..] = arg_path.segments
&& &first_arg_ps.ident == first_bind_ident
{
true
- }
- else {
+ } else {
false
}
};
if has_ident(first_arg) {
return true;
}
- if let hir::ExprKind::Tup(value) = &first_arg.kind && value.iter().any(has_ident) {
+ if let hir::ExprKind::Tup(value) = &first_arg.kind
+ && value.iter().any(has_ident)
+ {
return true;
}
}
diff --git a/src/tools/clippy/clippy_lints/src/single_call_fn.rs b/src/tools/clippy/clippy_lints/src/single_call_fn.rs
index 7bbe98e0a..0492df68d 100644
--- a/src/tools/clippy/clippy_lints/src/single_call_fn.rs
+++ b/src/tools/clippy/clippy_lints/src/single_call_fn.rs
@@ -23,7 +23,7 @@ declare_clippy_lint! {
/// Note: If this lint is used, prepare to allow this a lot.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// pub fn a<T>(t: &T)
/// where
/// T: AsRef<str>,
@@ -37,7 +37,7 @@ declare_clippy_lint! {
///
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// pub fn a<T>(t: &T)
/// where
/// T: AsRef<str>,
@@ -72,8 +72,8 @@ impl<'tcx> LateLintPass<'tcx> for SingleCallFn {
) {
if self.avoid_breaking_exported_api && cx.effective_visibilities.is_exported(def_id)
|| in_external_macro(cx.sess(), span)
- || is_from_proc_macro(cx, &(&kind, body, cx.tcx.local_def_id_to_hir_id(def_id), span))
|| is_in_test_function(cx.tcx, body.value.hir_id)
+ || is_from_proc_macro(cx, &(&kind, body, cx.tcx.local_def_id_to_hir_id(def_id), span))
{
return;
}
diff --git a/src/tools/clippy/clippy_lints/src/single_char_lifetime_names.rs b/src/tools/clippy/clippy_lints/src/single_char_lifetime_names.rs
index 3dc995e2f..74ee8ce2d 100644
--- a/src/tools/clippy/clippy_lints/src/single_char_lifetime_names.rs
+++ b/src/tools/clippy/clippy_lints/src/single_char_lifetime_names.rs
@@ -22,13 +22,13 @@ declare_clippy_lint! {
/// be obvious or, rarely, expressible in one character.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// struct DiagnosticCtx<'a> {
/// source: &'a str,
/// }
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// struct DiagnosticCtx<'src> {
/// source: &'src str,
/// }
diff --git a/src/tools/clippy/clippy_lints/src/single_range_in_vec_init.rs b/src/tools/clippy/clippy_lints/src/single_range_in_vec_init.rs
index 321c89889..099743d22 100644
--- a/src/tools/clippy/clippy_lints/src/single_range_in_vec_init.rs
+++ b/src/tools/clippy/clippy_lints/src/single_range_in_vec_init.rs
@@ -21,11 +21,11 @@ declare_clippy_lint! {
/// the end of the range to be the length instead.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let x = [0..200];
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// // If it was intended to include every element in the range...
/// let x = (0..200).collect::<Vec<i32>>();
/// // ...Or if 200 was meant to be the len
diff --git a/src/tools/clippy/clippy_lints/src/size_of_in_element_count.rs b/src/tools/clippy/clippy_lints/src/size_of_in_element_count.rs
index bd783b4e0..b940cac60 100644
--- a/src/tools/clippy/clippy_lints/src/size_of_in_element_count.rs
+++ b/src/tools/clippy/clippy_lints/src/size_of_in_element_count.rs
@@ -2,7 +2,6 @@
//! expecting a count of T
use clippy_utils::diagnostics::span_lint_and_help;
-use clippy_utils::{match_def_path, paths};
use if_chain::if_chain;
use rustc_hir::{BinOpKind, Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
@@ -67,16 +66,6 @@ fn get_pointee_ty_and_count_expr<'tcx>(
cx: &LateContext<'tcx>,
expr: &'tcx Expr<'_>,
) -> Option<(Ty<'tcx>, &'tcx Expr<'tcx>)> {
- const FUNCTIONS: [&[&str]; 8] = [
- &paths::PTR_COPY_NONOVERLAPPING,
- &paths::PTR_COPY,
- &paths::PTR_WRITE_BYTES,
- &paths::PTR_SWAP_NONOVERLAPPING,
- &paths::PTR_SLICE_FROM_RAW_PARTS,
- &paths::PTR_SLICE_FROM_RAW_PARTS_MUT,
- &paths::SLICE_FROM_RAW_PARTS,
- &paths::SLICE_FROM_RAW_PARTS_MUT,
- ];
const METHODS: [&str; 11] = [
"write_bytes",
"copy_to",
@@ -97,7 +86,16 @@ fn get_pointee_ty_and_count_expr<'tcx>(
if let ExprKind::Call(func, [.., count]) = expr.kind;
if let ExprKind::Path(ref func_qpath) = func.kind;
if let Some(def_id) = cx.qpath_res(func_qpath, func.hir_id).opt_def_id();
- if FUNCTIONS.iter().any(|func_path| match_def_path(cx, def_id, func_path));
+ if matches!(cx.tcx.get_diagnostic_name(def_id), Some(
+ sym::ptr_copy
+ | sym::ptr_copy_nonoverlapping
+ | sym::ptr_slice_from_raw_parts
+ | sym::ptr_slice_from_raw_parts_mut
+ | sym::ptr_swap_nonoverlapping
+ | sym::ptr_write_bytes
+ | sym::slice_from_raw_parts
+ | sym::slice_from_raw_parts_mut
+ ));
// Get the pointee type
if let Some(pointee_ty) = cx.typeck_results().node_args(func.hir_id).types().next();
diff --git a/src/tools/clippy/clippy_lints/src/size_of_ref.rs b/src/tools/clippy/clippy_lints/src/size_of_ref.rs
index 89ac8cd8c..7de029b7b 100644
--- a/src/tools/clippy/clippy_lints/src/size_of_ref.rs
+++ b/src/tools/clippy/clippy_lints/src/size_of_ref.rs
@@ -19,7 +19,7 @@ declare_clippy_lint! {
/// the reference.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// struct Foo {
/// buffer: [u8],
/// }
@@ -35,7 +35,7 @@ declare_clippy_lint! {
/// }
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// struct Foo {
/// buffer: [u8],
/// }
diff --git a/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs b/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs
index 9db18c297..2244eab96 100644
--- a/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs
+++ b/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs
@@ -31,7 +31,7 @@ declare_clippy_lint! {
/// The `resize` call first allocates memory (since `Vec::new()` did not), and only *then* zero-initializes it.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # use core::iter::repeat;
/// # let len = 4;
/// let mut vec1 = Vec::new();
@@ -45,7 +45,7 @@ declare_clippy_lint! {
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # let len = 4;
/// let mut vec1 = vec![0; len];
/// let mut vec2 = vec![0; len];
@@ -335,7 +335,7 @@ impl<'a, 'tcx> Visitor<'tcx> for VectorInitializationVisitor<'a, 'tcx> {
fn visit_block(&mut self, block: &'tcx Block<'_>) {
if self.initialization_found {
- if let Some(s) = block.stmts.get(0) {
+ if let Some(s) = block.stmts.first() {
self.visit_stmt(s);
}
diff --git a/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs b/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs
index 5f54a10d1..d07a44770 100644
--- a/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs
+++ b/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs
@@ -1,9 +1,11 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::is_from_proc_macro;
use rustc_errors::Applicability;
use rustc_hir::def::Res;
use rustc_hir::def_id::DefId;
use rustc_hir::{HirId, Path, PathSegment};
-use rustc_lint::{LateContext, LateLintPass};
+use rustc_lint::{LateContext, LateLintPass, LintContext};
+use rustc_middle::lint::in_external_macro;
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::symbol::kw;
use rustc_span::{sym, Span};
@@ -20,11 +22,11 @@ declare_clippy_lint! {
/// migrating to become `no_std` compatible.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// use std::hash::Hasher;
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// use core::hash::Hasher;
/// ```
#[clippy::version = "1.64.0"]
@@ -45,11 +47,11 @@ declare_clippy_lint! {
/// for crates migrating to become `no_std` compatible.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// use std::vec::Vec;
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # extern crate alloc;
/// use alloc::vec::Vec;
/// ```
@@ -71,12 +73,12 @@ declare_clippy_lint! {
/// is also useful for crates migrating to become `no_std` compatible.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # extern crate alloc;
/// use alloc::slice::from_ref;
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// use core::slice::from_ref;
/// ```
#[clippy::version = "1.64.0"]
@@ -99,19 +101,13 @@ impl<'tcx> LateLintPass<'tcx> for StdReexports {
if let Res::Def(_, def_id) = path.res
&& let Some(first_segment) = get_first_segment(path)
&& is_stable(cx, def_id)
+ && !in_external_macro(cx.sess(), path.span)
+ && !is_from_proc_macro(cx, &first_segment.ident)
{
let (lint, used_mod, replace_with) = match first_segment.ident.name {
sym::std => match cx.tcx.crate_name(def_id.krate) {
- sym::core => (
- STD_INSTEAD_OF_CORE,
- "std",
- "core",
- ),
- sym::alloc => (
- STD_INSTEAD_OF_ALLOC,
- "std",
- "alloc",
- ),
+ sym::core => (STD_INSTEAD_OF_CORE, "std", "core"),
+ sym::alloc => (STD_INSTEAD_OF_ALLOC, "std", "alloc"),
_ => {
self.prev_span = path.span;
return;
@@ -119,11 +115,7 @@ impl<'tcx> LateLintPass<'tcx> for StdReexports {
},
sym::alloc => {
if cx.tcx.crate_name(def_id.krate) == sym::core {
- (
- ALLOC_INSTEAD_OF_CORE,
- "alloc",
- "core",
- )
+ (ALLOC_INSTEAD_OF_CORE, "alloc", "core")
} else {
self.prev_span = path.span;
return;
@@ -139,7 +131,8 @@ impl<'tcx> LateLintPass<'tcx> for StdReexports {
&format!("used import from `{used_mod}` instead of `{replace_with}`"),
&format!("consider importing the item from `{replace_with}`"),
replace_with.to_string(),
- Applicability::MachineApplicable);
+ Applicability::MachineApplicable,
+ );
self.prev_span = path.span;
}
}
diff --git a/src/tools/clippy/clippy_lints/src/strings.rs b/src/tools/clippy/clippy_lints/src/strings.rs
index 76f463fff..a44adc938 100644
--- a/src/tools/clippy/clippy_lints/src/strings.rs
+++ b/src/tools/clippy/clippy_lints/src/strings.rs
@@ -26,7 +26,7 @@ declare_clippy_lint! {
/// `.push_str(_)` method is more readable.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let mut x = "Hello".to_owned();
/// x = x + ", World";
///
@@ -58,13 +58,13 @@ declare_clippy_lint! {
/// particular lint `allow` by default.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let x = "Hello".to_owned();
/// x + ", World";
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// let mut x = "Hello".to_owned();
/// x.push_str(", World");
/// ```
@@ -106,12 +106,12 @@ declare_clippy_lint! {
/// more readable than a function call.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let bstr = "a byte string".as_bytes();
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// let bstr = b"a byte string";
/// ```
#[clippy::version = "pre 1.29.0"]
@@ -231,12 +231,12 @@ declare_clippy_lint! {
/// It's unnecessary, the string can be used directly.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// std::str::from_utf8(&"Hello World!".as_bytes()[6..11]).unwrap();
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// &"Hello World!"[6..11];
/// ```
#[clippy::version = "1.50.0"]
@@ -387,12 +387,12 @@ declare_clippy_lint! {
/// expressed with `.to_owned()`.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// // example code where clippy issues a warning
/// let _ = "str".to_string();
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// // example code which does not raise clippy warning
/// let _ = "str".to_owned();
/// ```
@@ -435,13 +435,13 @@ declare_clippy_lint! {
/// When called on a `String` it only clones the `String`, which can be better expressed with `.clone()`.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// // example code where clippy issues a warning
/// let msg = String::from("Hello World");
/// let _ = msg.to_string();
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// // example code which does not raise clippy warning
/// let msg = String::from("Hello World");
/// let _ = msg.clone();
@@ -483,11 +483,11 @@ declare_clippy_lint! {
/// `split_whitespace` already ignores leading and trailing whitespace.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// " A B C ".trim().split_whitespace();
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// " A B C ".split_whitespace();
/// ```
#[clippy::version = "1.62.0"]
diff --git a/src/tools/clippy/clippy_lints/src/suspicious_doc_comments.rs b/src/tools/clippy/clippy_lints/src/suspicious_doc_comments.rs
index 8be4ec3dc..0abc199da 100644
--- a/src/tools/clippy/clippy_lints/src/suspicious_doc_comments.rs
+++ b/src/tools/clippy/clippy_lints/src/suspicious_doc_comments.rs
@@ -32,7 +32,7 @@ declare_clippy_lint! {
///
/// ### Example
/// In this example, the doc comment is attached to the *function*, rather than the *module*.
- /// ```rust
+ /// ```no_run
/// pub mod util {
/// ///! This module contains utility functions.
///
@@ -41,7 +41,7 @@ declare_clippy_lint! {
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// pub mod util {
/// //! This module contains utility functions.
///
diff --git a/src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs b/src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs
index d10f10ef8..bb8cde5b9 100644
--- a/src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs
+++ b/src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs
@@ -28,7 +28,7 @@ declare_clippy_lint! {
/// unusual that happens to look like a typo.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// struct Vec3 {
/// x: f64,
/// y: f64,
@@ -45,7 +45,7 @@ declare_clippy_lint! {
/// }
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # struct Vec3 {
/// # x: f64,
/// # y: f64,
@@ -578,7 +578,7 @@ fn ident_difference_expr_with_base_location(
| (Assign(_, _, _), Assign(_, _, _))
| (TryBlock(_), TryBlock(_))
| (Await(_, _), Await(_, _))
- | (Async(_, _), Async(_, _))
+ | (Gen(_, _, _), Gen(_, _, _))
| (Block(_, _), Block(_, _))
| (Closure(_), Closure(_))
| (Match(_, _), Match(_, _))
diff --git a/src/tools/clippy/clippy_lints/src/suspicious_xor_used_as_pow.rs b/src/tools/clippy/clippy_lints/src/suspicious_xor_used_as_pow.rs
index 8e156b882..4340c23f8 100644
--- a/src/tools/clippy/clippy_lints/src/suspicious_xor_used_as_pow.rs
+++ b/src/tools/clippy/clippy_lints/src/suspicious_xor_used_as_pow.rs
@@ -14,11 +14,11 @@ declare_clippy_lint! {
/// ### Why is this bad?
/// It's most probably a typo and may lead to unexpected behaviours.
/// ### Example
- /// ```rust
+ /// ```no_run
/// let x = 3_i32 ^ 4_i32;
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// let x = 3_i32.pow(4);
/// ```
#[clippy::version = "1.67.0"]
@@ -33,26 +33,23 @@ impl LateLintPass<'_> for ConfusingXorAndPow {
if !in_external_macro(cx.sess(), expr.span)
&& let ExprKind::Binary(op, left, right) = &expr.kind
&& op.node == BinOpKind::BitXor
- && left.span.ctxt() == right.span.ctxt()
+ && left.span.eq_ctxt(right.span)
&& let ExprKind::Lit(lit_left) = &left.kind
&& let ExprKind::Lit(lit_right) = &right.kind
&& matches!(lit_right.node, LitKind::Int(..) | LitKind::Float(..))
&& matches!(lit_left.node, LitKind::Int(..) | LitKind::Float(..))
- && NumericLiteral::from_lit_kind(&snippet(cx, lit_right.span, ".."), &lit_right.node).is_some_and(|x| x.is_decimal())
- {
- span_lint_and_sugg(
- cx,
- SUSPICIOUS_XOR_USED_AS_POW,
- expr.span,
- "`^` is not the exponentiation operator",
- "did you mean to write",
- format!(
- "{}.pow({})",
- lit_left.node,
- lit_right.node
- ),
- Applicability::MaybeIncorrect,
- );
- }
+ && NumericLiteral::from_lit_kind(&snippet(cx, lit_right.span, ".."), &lit_right.node)
+ .is_some_and(|x| x.is_decimal())
+ {
+ span_lint_and_sugg(
+ cx,
+ SUSPICIOUS_XOR_USED_AS_POW,
+ expr.span,
+ "`^` is not the exponentiation operator",
+ "did you mean to write",
+ format!("{}.pow({})", lit_left.node, lit_right.node),
+ Applicability::MaybeIncorrect,
+ );
+ }
}
}
diff --git a/src/tools/clippy/clippy_lints/src/swap.rs b/src/tools/clippy/clippy_lints/src/swap.rs
index 548fabb8b..660e6835e 100644
--- a/src/tools/clippy/clippy_lints/src/swap.rs
+++ b/src/tools/clippy/clippy_lints/src/swap.rs
@@ -25,7 +25,7 @@ declare_clippy_lint! {
/// without deinitializing or copying either variable.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let mut a = 42;
/// let mut b = 1337;
///
@@ -34,7 +34,7 @@ declare_clippy_lint! {
/// a = t;
/// ```
/// Use std::mem::swap():
- /// ```rust
+ /// ```no_run
/// let mut a = 1;
/// let mut b = 2;
/// std::mem::swap(&mut a, &mut b);
@@ -53,14 +53,14 @@ declare_clippy_lint! {
/// This looks like a failed attempt to swap.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # let mut a = 1;
/// # let mut b = 2;
/// a = b;
/// b = a;
/// ```
/// If swapping is intended, use `swap()` instead:
- /// ```rust
+ /// ```no_run
/// # let mut a = 1;
/// # let mut b = 2;
/// std::mem::swap(&mut a, &mut b);
@@ -232,7 +232,7 @@ fn is_same(cx: &LateContext<'_>, lhs: ExprOrIdent<'_>, rhs: &Expr<'_>) -> bool {
} else {
false
}
- }
+ },
}
}
diff --git a/src/tools/clippy/clippy_lints/src/swap_ptr_to_ref.rs b/src/tools/clippy/clippy_lints/src/swap_ptr_to_ref.rs
index d085dda35..6a6c94425 100644
--- a/src/tools/clippy/clippy_lints/src/swap_ptr_to_ref.rs
+++ b/src/tools/clippy/clippy_lints/src/swap_ptr_to_ref.rs
@@ -1,11 +1,11 @@
use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::path_def_id;
use clippy_utils::source::snippet_with_context;
-use clippy_utils::{match_def_path, path_def_id, paths};
use rustc_errors::Applicability;
use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, UnOp};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::{Span, SyntaxContext};
+use rustc_span::{sym, Span, SyntaxContext};
declare_clippy_lint! {
/// ### What it does
@@ -16,7 +16,7 @@ declare_clippy_lint! {
/// other. This would then lead to undefined behavior.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// unsafe fn swap(x: &[*mut u32], y: &[*mut u32]) {
/// for (&x, &y) in x.iter().zip(y) {
/// core::mem::swap(&mut *x, &mut *y);
@@ -24,7 +24,7 @@ declare_clippy_lint! {
/// }
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// unsafe fn swap(x: &[*mut u32], y: &[*mut u32]) {
/// for (&x, &y) in x.iter().zip(y) {
/// core::ptr::swap(x, y);
@@ -42,7 +42,7 @@ impl LateLintPass<'_> for SwapPtrToRef {
fn check_expr(&mut self, cx: &LateContext<'_>, e: &Expr<'_>) {
if let ExprKind::Call(fn_expr, [arg1, arg2]) = e.kind
&& let Some(fn_id) = path_def_id(cx, fn_expr)
- && match_def_path(cx, fn_id, &paths::MEM_SWAP)
+ && cx.tcx.is_diagnostic_item(sym::mem_swap, fn_id)
&& let ctxt = e.span.ctxt()
&& let (from_ptr1, arg1_span) = is_ptr_to_ref(cx, arg1, ctxt)
&& let (from_ptr2, arg2_span) = is_ptr_to_ref(cx, arg2, ctxt)
@@ -58,9 +58,14 @@ impl LateLintPass<'_> for SwapPtrToRef {
let mut app = Applicability::MachineApplicable;
let snip1 = snippet_with_context(cx, arg1_span.unwrap_or(arg1.span), ctxt, "..", &mut app).0;
let snip2 = snippet_with_context(cx, arg2_span.unwrap_or(arg2.span), ctxt, "..", &mut app).0;
- diag.span_suggestion(e.span, "use ptr::swap", format!("core::ptr::swap({snip1}, {snip2})"), app);
+ diag.span_suggestion(
+ e.span,
+ "use ptr::swap",
+ format!("core::ptr::swap({snip1}, {snip2})"),
+ app,
+ );
}
- }
+ },
);
}
}
@@ -73,7 +78,10 @@ fn is_ptr_to_ref(cx: &LateContext<'_>, e: &Expr<'_>, ctxt: SyntaxContext) -> (bo
&& let ExprKind::Unary(UnOp::Deref, derefed_expr) = borrowed_expr.kind
&& cx.typeck_results().expr_ty(derefed_expr).is_unsafe_ptr()
{
- (true, (borrowed_expr.span.ctxt() == ctxt || derefed_expr.span.ctxt() == ctxt).then_some(derefed_expr.span))
+ (
+ true,
+ (borrowed_expr.span.ctxt() == ctxt || derefed_expr.span.ctxt() == ctxt).then_some(derefed_expr.span),
+ )
} else {
(false, None)
}
diff --git a/src/tools/clippy/clippy_lints/src/tabs_in_doc_comments.rs b/src/tools/clippy/clippy_lints/src/tabs_in_doc_comments.rs
index e223aea29..dcf1fac02 100644
--- a/src/tools/clippy/clippy_lints/src/tabs_in_doc_comments.rs
+++ b/src/tools/clippy/clippy_lints/src/tabs_in_doc_comments.rs
@@ -3,7 +3,7 @@ use rustc_ast::ast;
use rustc_errors::Applicability;
use rustc_lint::{EarlyContext, EarlyLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::source_map::{BytePos, Span};
+use rustc_span::{BytePos, Span};
declare_clippy_lint! {
/// ### What it does
@@ -16,7 +16,7 @@ declare_clippy_lint! {
/// display settings of the author and reader differ.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// ///
/// /// Struct to hold two strings:
/// /// - first one
@@ -34,7 +34,7 @@ declare_clippy_lint! {
/// ```
///
/// Will be converted to:
- /// ```rust
+ /// ```no_run
/// ///
/// /// Struct to hold two strings:
/// /// - first one
diff --git a/src/tools/clippy/clippy_lints/src/temporary_assignment.rs b/src/tools/clippy/clippy_lints/src/temporary_assignment.rs
index b6b653f66..c717ccc35 100644
--- a/src/tools/clippy/clippy_lints/src/temporary_assignment.rs
+++ b/src/tools/clippy/clippy_lints/src/temporary_assignment.rs
@@ -14,7 +14,7 @@ declare_clippy_lint! {
/// updated, why not write the structure you want in the first place?
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// (0, 0).0 = 1
/// ```
#[clippy::version = "pre 1.29.0"]
diff --git a/src/tools/clippy/clippy_lints/src/tests_outside_test_module.rs b/src/tools/clippy/clippy_lints/src/tests_outside_test_module.rs
index b356666d8..0cfb1c125 100644
--- a/src/tools/clippy/clippy_lints/src/tests_outside_test_module.rs
+++ b/src/tools/clippy/clippy_lints/src/tests_outside_test_module.rs
@@ -15,7 +15,7 @@ declare_clippy_lint! {
/// The idiomatic (and more performant) way of writing tests is inside a testing module (flagged with `#[cfg(test)]`),
/// having test functions outside of this module is confusing and may lead to them being "hidden".
/// ### Example
- /// ```rust
+ /// ```no_run
/// #[test]
/// fn my_cool_test() {
/// // [...]
@@ -28,7 +28,7 @@ declare_clippy_lint! {
///
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// #[cfg(test)]
/// mod tests {
/// #[test]
diff --git a/src/tools/clippy/clippy_lints/src/to_digit_is_some.rs b/src/tools/clippy/clippy_lints/src/to_digit_is_some.rs
index f1b703fde..a171d225f 100644
--- a/src/tools/clippy/clippy_lints/src/to_digit_is_some.rs
+++ b/src/tools/clippy/clippy_lints/src/to_digit_is_some.rs
@@ -17,13 +17,13 @@ declare_clippy_lint! {
/// more straight forward to use the dedicated `is_digit` method.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # let c = 'c';
/// # let radix = 10;
/// let is_digit = c.to_digit(radix).is_some();
/// ```
/// can be written as:
- /// ```
+ /// ```no_run
/// # let c = 'c';
/// # let radix = 10;
/// let is_digit = c.is_digit(radix);
diff --git a/src/tools/clippy/clippy_lints/src/trailing_empty_array.rs b/src/tools/clippy/clippy_lints/src/trailing_empty_array.rs
index bb9da3a20..87181adc2 100644
--- a/src/tools/clippy/clippy_lints/src/trailing_empty_array.rs
+++ b/src/tools/clippy/clippy_lints/src/trailing_empty_array.rs
@@ -13,7 +13,7 @@ declare_clippy_lint! {
/// Zero-sized arrays aren't very useful in Rust itself, so such a struct is likely being created to pass to C code or in some other situation where control over memory layout matters (for example, in conjunction with manual allocation to make it easy to compute the offset of the array). Either way, `#[repr(C)]` (or another `repr` attribute) is needed.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// struct RarelyUseful {
/// some_field: u32,
/// last: [u32; 0],
@@ -21,7 +21,7 @@ declare_clippy_lint! {
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// #[repr(C)]
/// struct MoreOftenUseful {
/// some_field: usize,
diff --git a/src/tools/clippy/clippy_lints/src/trait_bounds.rs b/src/tools/clippy/clippy_lints/src/trait_bounds.rs
index 6db330dfa..f065d215e 100644
--- a/src/tools/clippy/clippy_lints/src/trait_bounds.rs
+++ b/src/tools/clippy/clippy_lints/src/trait_bounds.rs
@@ -1,5 +1,5 @@
+use clippy_config::msrvs::{self, Msrv};
use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg};
-use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::source::{snippet, snippet_opt, snippet_with_applicability};
use clippy_utils::{is_from_proc_macro, SpanlessEq, SpanlessHash};
use core::hash::{Hash, Hasher};
@@ -27,12 +27,12 @@ declare_clippy_lint! {
/// less readable than combining the bounds
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// pub fn foo<T>(t: T) where T: Copy, T: Clone {}
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// pub fn foo<T>(t: T) where T: Copy + Clone {}
/// ```
#[clippy::version = "1.38.0"]
@@ -51,12 +51,12 @@ declare_clippy_lint! {
/// less readable than specifying them only once.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// fn func<T: Clone + Default>(arg: T) where T: Clone + Default {}
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # mod hidden {
/// fn func<T: Clone + Default>(arg: T) {}
/// # }
@@ -66,19 +66,19 @@ declare_clippy_lint! {
/// fn func<T>(arg: T) where T: Clone + Default {}
/// ```
///
- /// ```rust
+ /// ```no_run
/// fn foo<T: Default + Default>(bar: T) {}
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// fn foo<T: Default>(bar: T) {}
/// ```
///
- /// ```rust
+ /// ```no_run
/// fn foo<T>(bar: T) where T: Default + Default {}
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// fn foo<T>(bar: T) where T: Default {}
/// ```
#[clippy::version = "1.47.0"]
diff --git a/src/tools/clippy/clippy_lints/src/transmute/mod.rs b/src/tools/clippy/clippy_lints/src/transmute/mod.rs
index 0dc30f7a9..6eec40cb5 100644
--- a/src/tools/clippy/clippy_lints/src/transmute/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/transmute/mod.rs
@@ -17,8 +17,8 @@ mod useless_transmute;
mod utils;
mod wrong_transmute;
+use clippy_config::msrvs::Msrv;
use clippy_utils::in_constant;
-use clippy_utils::msrvs::Msrv;
use if_chain::if_chain;
use rustc_hir::{Expr, ExprKind, QPath};
use rustc_lint::{LateContext, LateLintPass};
@@ -78,12 +78,12 @@ declare_clippy_lint! {
///
/// ### Example
///
- /// ```rust
+ /// ```no_run
/// # let p: *const [i32] = &[];
/// unsafe { std::mem::transmute::<*const [i32], *const [u16]>(p) };
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # let p: *const [i32] = &[];
/// p as *const [u16];
/// ```
@@ -159,7 +159,7 @@ declare_clippy_lint! {
/// [`from_u32_unchecked`]: https://doc.rust-lang.org/std/char/fn.from_u32_unchecked.html
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let x = 1_u32;
/// unsafe {
/// let _: char = std::mem::transmute(x); // where x: u32
@@ -193,7 +193,7 @@ declare_clippy_lint! {
/// [`from_utf8_unchecked`]: https://doc.rust-lang.org/std/str/fn.from_utf8_unchecked.html
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let b: &[u8] = &[1_u8, 2_u8];
/// unsafe {
/// let _: &str = std::mem::transmute(b); // where b: &[u8]
@@ -216,7 +216,7 @@ declare_clippy_lint! {
/// This might result in an invalid in-memory representation of a `bool`.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let x = 1_u8;
/// unsafe {
/// let _: bool = std::mem::transmute(x); // where x: u8
@@ -240,7 +240,7 @@ declare_clippy_lint! {
/// and safe.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// unsafe {
/// let _: f32 = std::mem::transmute(1_u32); // where x: u32
/// }
@@ -264,12 +264,12 @@ declare_clippy_lint! {
/// elsewhere. `new_unchecked` only works for the appropriate types instead.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # use core::num::NonZeroU32;
/// let _non_zero: NonZeroU32 = unsafe { std::mem::transmute(123) };
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # use core::num::NonZeroU32;
/// let _non_zero = unsafe { NonZeroU32::new_unchecked(123) };
/// ```
@@ -288,7 +288,7 @@ declare_clippy_lint! {
/// and safe.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// unsafe {
/// let _: u32 = std::mem::transmute(1f32);
/// }
@@ -311,7 +311,7 @@ declare_clippy_lint! {
/// is intuitive and safe.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// unsafe {
/// let x: [u8; 8] = std::mem::transmute(1i64);
/// }
@@ -335,7 +335,7 @@ declare_clippy_lint! {
/// written as casts.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let ptr = &1u32 as *const u32;
/// unsafe {
/// // pointer-to-pointer transmute
@@ -366,7 +366,7 @@ declare_clippy_lint! {
/// collection, so we just lint the ones that come with `std`.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// // different size, therefore likely out-of-bounds memory access
/// // You absolutely do not want this in your code!
/// unsafe {
@@ -376,7 +376,7 @@ declare_clippy_lint! {
///
/// You must always iterate, map and collect the values:
///
- /// ```rust
+ /// ```no_run
/// vec![2_u16].into_iter().map(u32::from).collect::<Vec<_>>();
/// ```
#[clippy::version = "1.40.0"]
@@ -398,12 +398,12 @@ declare_clippy_lint! {
/// [#8496](https://github.com/rust-lang/rust-clippy/issues/8496) for more details.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// struct Foo<T>(u32, T);
/// let _ = unsafe { core::mem::transmute::<Foo<u32>, Foo<i32>>(Foo(0u32, 0u32)) };
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// #[repr(C)]
/// struct Foo<T>(u32, T);
/// let _ = unsafe { core::mem::transmute::<Foo<u32>, Foo<i32>>(Foo(0u32, 0u32)) };
@@ -427,7 +427,7 @@ declare_clippy_lint! {
/// call, aren't detectable yet.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let null_ref: &u64 = unsafe { std::mem::transmute(0 as *const u64) };
/// ```
#[clippy::version = "1.35.0"]
@@ -451,11 +451,11 @@ declare_clippy_lint! {
/// call, aren't detectable yet.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let null_fn: fn() = unsafe { std::mem::transmute( std::ptr::null::<()>() ) };
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// let null_fn: Option<fn()> = None;
/// ```
#[clippy::version = "1.68.0"]
diff --git a/src/tools/clippy/clippy_lints/src/transmute/transmute_ptr_to_ref.rs b/src/tools/clippy/clippy_lints/src/transmute/transmute_ptr_to_ref.rs
index 6bdb9aa5a..4ab3afbe7 100644
--- a/src/tools/clippy/clippy_lints/src/transmute/transmute_ptr_to_ref.rs
+++ b/src/tools/clippy/clippy_lints/src/transmute/transmute_ptr_to_ref.rs
@@ -1,6 +1,6 @@
use super::TRANSMUTE_PTR_TO_REF;
+use clippy_config::msrvs::{self, Msrv};
use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::sugg;
use rustc_errors::Applicability;
diff --git a/src/tools/clippy/clippy_lints/src/transmute/transmute_undefined_repr.rs b/src/tools/clippy/clippy_lints/src/transmute/transmute_undefined_repr.rs
index c61eb0a93..7c2223ca3 100644
--- a/src/tools/clippy/clippy_lints/src/transmute/transmute_undefined_repr.rs
+++ b/src/tools/clippy/clippy_lints/src/transmute/transmute_undefined_repr.rs
@@ -98,17 +98,17 @@ pub(super) fn check<'tcx>(
},
(ReducedTy::UnorderedFields(from_ty), ReducedTy::UnorderedFields(to_ty)) if from_ty != to_ty => {
- let same_adt_did = if let (ty::Adt(from_def, from_subs), ty::Adt(to_def, to_subs))
- = (from_ty.kind(), to_ty.kind())
- && from_def == to_def
- {
- if same_except_params(from_subs, to_subs) {
- return false;
- }
- Some(from_def.did())
- } else {
- None
- };
+ let same_adt_did = if let (ty::Adt(from_def, from_subs), ty::Adt(to_def, to_subs)) =
+ (from_ty.kind(), to_ty.kind())
+ && from_def == to_def
+ {
+ if same_except_params(from_subs, to_subs) {
+ return false;
+ }
+ Some(from_def.did())
+ } else {
+ None
+ };
span_lint_and_then(
cx,
TRANSMUTE_UNDEFINED_REPR,
diff --git a/src/tools/clippy/clippy_lints/src/transmute/transmuting_null.rs b/src/tools/clippy/clippy_lints/src/transmute/transmuting_null.rs
index 770914e99..471bd44b5 100644
--- a/src/tools/clippy/clippy_lints/src/transmute/transmuting_null.rs
+++ b/src/tools/clippy/clippy_lints/src/transmute/transmuting_null.rs
@@ -16,8 +16,8 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arg: &'t
}
// Catching transmute over constants that resolve to `null`.
- if let ExprKind::Path(ref _qpath) = arg.kind &&
- let Some(Constant::RawPtr(0)) = constant(cx, cx.typeck_results(), arg)
+ if let ExprKind::Path(ref _qpath) = arg.kind
+ && let Some(Constant::RawPtr(0)) = constant(cx, cx.typeck_results(), arg)
{
span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG);
return true;
@@ -25,15 +25,17 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arg: &'t
// Catching:
// `std::mem::transmute(0 as *const i32)`
- if let ExprKind::Cast(inner_expr, _cast_ty) = arg.kind && is_integer_literal(inner_expr, 0) {
+ if let ExprKind::Cast(inner_expr, _cast_ty) = arg.kind
+ && is_integer_literal(inner_expr, 0)
+ {
span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG);
return true;
}
// Catching:
// `std::mem::transmute(std::ptr::null::<i32>())`
- if let ExprKind::Call(func1, []) = arg.kind &&
- is_path_diagnostic_item(cx, func1, sym::ptr_null)
+ if let ExprKind::Call(func1, []) = arg.kind
+ && is_path_diagnostic_item(cx, func1, sym::ptr_null)
{
span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG);
return true;
diff --git a/src/tools/clippy/clippy_lints/src/tuple_array_conversions.rs b/src/tools/clippy/clippy_lints/src/tuple_array_conversions.rs
index c12519d72..642e39e82 100644
--- a/src/tools/clippy/clippy_lints/src/tuple_array_conversions.rs
+++ b/src/tools/clippy/clippy_lints/src/tuple_array_conversions.rs
@@ -1,5 +1,5 @@
+use clippy_config::msrvs::{self, Msrv};
use clippy_utils::diagnostics::span_lint_and_help;
-use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::visitors::for_each_local_use_after_expr;
use clippy_utils::{is_from_proc_macro, path_to_local};
use itertools::Itertools;
diff --git a/src/tools/clippy/clippy_lints/src/types/mod.rs b/src/tools/clippy/clippy_lints/src/types/mod.rs
index 71a4b3fba..6a6160c49 100644
--- a/src/tools/clippy/clippy_lints/src/types/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/types/mod.rs
@@ -18,7 +18,7 @@ use rustc_hir::{
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::def_id::LocalDefId;
-use rustc_span::source_map::Span;
+use rustc_span::Span;
declare_clippy_lint! {
/// ### What it does
@@ -64,7 +64,7 @@ declare_clippy_lint! {
/// 1st comment).
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// struct X {
/// values: Vec<Box<i32>>,
/// }
@@ -72,7 +72,7 @@ declare_clippy_lint! {
///
/// Better:
///
- /// ```rust
+ /// ```no_run
/// struct X {
/// values: Vec<i32>,
/// }
@@ -97,7 +97,7 @@ declare_clippy_lint! {
/// consider a custom `enum` instead, with clear names for each case.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// fn get_data() -> Option<Option<u32>> {
/// None
/// }
@@ -105,7 +105,7 @@ declare_clippy_lint! {
///
/// Better:
///
- /// ```rust
+ /// ```no_run
/// pub enum Contents {
/// Data(Vec<u8>), // Was Some(Some(Vec<u8>))
/// NotYetFetched, // Was Some(None)
@@ -152,7 +152,7 @@ declare_clippy_lint! {
/// `LinkedList` makes sense are few and far between, but they can still happen.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # use std::collections::LinkedList;
/// let x: LinkedList<usize> = LinkedList::new();
/// ```
@@ -197,14 +197,14 @@ declare_clippy_lint! {
/// `Arc<Arc<T>>`, `Arc<Box<T>>`, `Box<&T>`, `Box<Rc<T>>`, `Box<Arc<T>>`, `Box<Box<T>>`, add an unnecessary level of indirection.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # use std::rc::Rc;
/// fn foo(bar: Rc<&usize>) {}
/// ```
///
/// Better:
///
- /// ```rust
+ /// ```no_run
/// fn foo(bar: &usize) {}
/// ```
#[clippy::version = "1.44.0"]
@@ -258,7 +258,7 @@ declare_clippy_lint! {
/// using a `type` definition to simplify them.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # use std::rc::Rc;
/// struct Foo {
/// inner: Rc<Vec<Vec<Box<(u32, u32, u32, u32)>>>>,
@@ -578,7 +578,7 @@ impl Types {
}
}
-#[allow(clippy::struct_excessive_bools)]
+#[allow(clippy::struct_excessive_bools, clippy::struct_field_names)]
#[derive(Clone, Copy, Default)]
struct CheckTyContext {
is_in_trait_impl: bool,
diff --git a/src/tools/clippy/clippy_lints/src/types/utils.rs b/src/tools/clippy/clippy_lints/src/types/utils.rs
index a30748db8..39469841b 100644
--- a/src/tools/clippy/clippy_lints/src/types/utils.rs
+++ b/src/tools/clippy/clippy_lints/src/types/utils.rs
@@ -2,7 +2,7 @@ use clippy_utils::last_path_segment;
use if_chain::if_chain;
use rustc_hir::{GenericArg, GenericArgsParentheses, QPath, TyKind};
use rustc_lint::LateContext;
-use rustc_span::source_map::Span;
+use rustc_span::Span;
pub(super) fn match_borrows_parameter(_cx: &LateContext<'_>, qpath: &QPath<'_>) -> Option<Span> {
let last = last_path_segment(qpath);
diff --git a/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs b/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs
index 6193fdeb4..32aebdd8c 100644
--- a/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs
+++ b/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs
@@ -44,14 +44,14 @@ declare_clippy_lint! {
/// and bugs.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// use std::ptr::NonNull;
/// let a = &mut 42;
///
/// let ptr = unsafe { NonNull::new_unchecked(a) };
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// use std::ptr::NonNull;
/// let a = &mut 42;
///
@@ -72,7 +72,7 @@ declare_clippy_lint! {
/// describe safety invariants.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// use std::ptr::NonNull;
/// let a = &mut 42;
///
@@ -80,7 +80,7 @@ declare_clippy_lint! {
/// let ptr = NonNull::new(a).unwrap();
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// use std::ptr::NonNull;
/// let a = &mut 42;
///
@@ -638,7 +638,9 @@ fn get_body_search_span(cx: &LateContext<'_>) -> Option<Span> {
fn span_has_safety_comment(cx: &LateContext<'_>, span: Span) -> bool {
let source_map = cx.sess().source_map();
let ctxt = span.ctxt();
- if ctxt.is_root() && let Some(search_span) = get_body_search_span(cx) {
+ if ctxt.is_root()
+ && let Some(search_span) = get_body_search_span(cx)
+ {
if let Ok(unsafe_line) = source_map.lookup_line(span.lo())
&& let Some(body_span) = walk_span_to_context(search_span, SyntaxContext::root())
&& let Ok(body_line) = source_map.lookup_line(body_span.lo())
@@ -648,11 +650,13 @@ fn span_has_safety_comment(cx: &LateContext<'_>, span: Span) -> bool {
// Get the text from the start of function body to the unsafe block.
// fn foo() { some_stuff; unsafe { stuff }; other_stuff; }
// ^-------------^
- body_line.line < unsafe_line.line && text_has_safety_comment(
- src,
- &unsafe_line.sf.lines()[body_line.line + 1..=unsafe_line.line],
- unsafe_line.sf.start_pos,
- ).is_some()
+ body_line.line < unsafe_line.line
+ && text_has_safety_comment(
+ src,
+ &unsafe_line.sf.lines()[body_line.line + 1..=unsafe_line.line],
+ unsafe_line.sf.start_pos,
+ )
+ .is_some()
} else {
// Problem getting source text. Pretend a comment was found.
true
diff --git a/src/tools/clippy/clippy_lints/src/unicode.rs b/src/tools/clippy/clippy_lints/src/unicode.rs
index e275bfd37..b824deac2 100644
--- a/src/tools/clippy/clippy_lints/src/unicode.rs
+++ b/src/tools/clippy/clippy_lints/src/unicode.rs
@@ -7,7 +7,7 @@ use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, HirId};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::source_map::Span;
+use rustc_span::Span;
use unicode_normalization::UnicodeNormalization;
declare_clippy_lint! {
@@ -39,12 +39,12 @@ declare_clippy_lint! {
/// requirements, activating this lint could be useful.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let x = String::from("€");
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// let x = String::from("\u{20ac}");
/// ```
#[clippy::version = "pre 1.29.0"]
diff --git a/src/tools/clippy/clippy_lints/src/uninit_vec.rs b/src/tools/clippy/clippy_lints/src/uninit_vec.rs
index 6756df8e7..72569e10f 100644
--- a/src/tools/clippy/clippy_lints/src/uninit_vec.rs
+++ b/src/tools/clippy/clippy_lints/src/uninit_vec.rs
@@ -201,7 +201,7 @@ fn extract_set_len_self<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'_>) -> Opt
let expr = peel_hir_expr_while(expr, |e| {
if let ExprKind::Block(block, _) = e.kind {
// Extract the first statement/expression
- match (block.stmts.get(0).map(|stmt| &stmt.kind), block.expr) {
+ match (block.stmts.first().map(|stmt| &stmt.kind), block.expr) {
(None, Some(expr)) => Some(expr),
(Some(StmtKind::Expr(expr) | StmtKind::Semi(expr)), _) => Some(expr),
_ => None,
diff --git a/src/tools/clippy/clippy_lints/src/unit_return_expecting_ord.rs b/src/tools/clippy/clippy_lints/src/unit_return_expecting_ord.rs
index de4b8738e..e76cc65fd 100644
--- a/src/tools/clippy/clippy_lints/src/unit_return_expecting_ord.rs
+++ b/src/tools/clippy/clippy_lints/src/unit_return_expecting_ord.rs
@@ -25,7 +25,7 @@ declare_clippy_lint! {
/// way of specifying this without triggering needless_return lint
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let mut twins = vec![(1, 1), (2, 2)];
/// twins.sort_by_key(|x| { x.1; });
/// ```
diff --git a/src/tools/clippy/clippy_lints/src/unit_types/let_unit_value.rs b/src/tools/clippy/clippy_lints/src/unit_types/let_unit_value.rs
index e7915953d..ef67f4b04 100644
--- a/src/tools/clippy/clippy_lints/src/unit_types/let_unit_value.rs
+++ b/src/tools/clippy/clippy_lints/src/unit_types/let_unit_value.rs
@@ -24,7 +24,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, local: &'tcx Local<'_>) {
&& expr_needs_inferred_result(cx, init)
{
if !matches!(local.pat.kind, PatKind::Wild)
- && !matches!(local.pat.kind, PatKind::Tuple([], ddpos) if ddpos.as_opt_usize().is_none())
+ && !matches!(local.pat.kind, PatKind::Tuple([], ddpos) if ddpos.as_opt_usize().is_none())
{
span_lint_and_then(
cx,
@@ -43,7 +43,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, local: &'tcx Local<'_>) {
}
} else {
if let ExprKind::Match(_, _, MatchSource::AwaitDesugar) = init.kind {
- return
+ return;
}
span_lint_and_then(
@@ -55,12 +55,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, local: &'tcx Local<'_>) {
if let Some(expr) = &local.init {
let mut app = Applicability::MachineApplicable;
let snip = snippet_with_context(cx, expr.span, local.span.ctxt(), "()", &mut app).0;
- diag.span_suggestion(
- local.span,
- "omit the `let` binding",
- format!("{snip};"),
- app,
- );
+ diag.span_suggestion(local.span, "omit the `let` binding", format!("{snip};"), app);
}
},
);
diff --git a/src/tools/clippy/clippy_lints/src/unit_types/mod.rs b/src/tools/clippy/clippy_lints/src/unit_types/mod.rs
index 546242ebd..884c6ca4d 100644
--- a/src/tools/clippy/clippy_lints/src/unit_types/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/unit_types/mod.rs
@@ -16,7 +16,7 @@ declare_clippy_lint! {
/// binding one is kind of pointless.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let x = {
/// 1;
/// };
@@ -38,7 +38,7 @@ declare_clippy_lint! {
/// adds semicolons at the end of the operands.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # fn foo() {};
/// # fn bar() {};
/// # fn baz() {};
@@ -51,7 +51,7 @@ declare_clippy_lint! {
/// }
/// ```
/// is equal to
- /// ```rust
+ /// ```no_run
/// # fn foo() {};
/// # fn bar() {};
/// # fn baz() {};
@@ -63,7 +63,7 @@ declare_clippy_lint! {
/// ```
///
/// For asserts:
- /// ```rust
+ /// ```no_run
/// # fn foo() {};
/// # fn bar() {};
/// assert_eq!({ foo(); }, { bar(); });
diff --git a/src/tools/clippy/clippy_lints/src/unnamed_address.rs b/src/tools/clippy/clippy_lints/src/unnamed_address.rs
index dea8a1e35..e7355f923 100644
--- a/src/tools/clippy/clippy_lints/src/unnamed_address.rs
+++ b/src/tools/clippy/clippy_lints/src/unnamed_address.rs
@@ -1,10 +1,10 @@
use clippy_utils::diagnostics::{span_lint, span_lint_and_help};
-use clippy_utils::{match_def_path, paths};
use if_chain::if_chain;
use rustc_hir::{BinOpKind, Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty;
use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::sym;
declare_clippy_lint! {
/// ### What it does
@@ -16,7 +16,7 @@ declare_clippy_lint! {
/// the same address after being merged together.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// type F = fn();
/// fn a() {}
/// let f: F = a;
@@ -96,7 +96,7 @@ impl LateLintPass<'_> for UnnamedAddress {
if let ExprKind::Call(func, [ref _left, ref _right]) = expr.kind;
if let ExprKind::Path(ref func_qpath) = func.kind;
if let Some(def_id) = cx.qpath_res(func_qpath, func.hir_id).opt_def_id();
- if match_def_path(cx, def_id, &paths::PTR_EQ);
+ if cx.tcx.is_diagnostic_item(sym::ptr_eq, def_id);
let ty_param = cx.typeck_results().node_args(func.hir_id).type_at(0);
if ty_param.is_trait();
then {
diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_box_returns.rs b/src/tools/clippy/clippy_lints/src/unnecessary_box_returns.rs
index ed2ef5063..ca159eb4d 100644
--- a/src/tools/clippy/clippy_lints/src/unnecessary_box_returns.rs
+++ b/src/tools/clippy/clippy_lints/src/unnecessary_box_returns.rs
@@ -22,13 +22,13 @@ declare_clippy_lint! {
/// `Box<T>` been dropped.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// fn foo() -> Box<String> {
/// Box::new(String::from("Hello, world!"))
/// }
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// fn foo() -> String {
/// String::from("Hello, world!")
/// }
diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_map_on_constructor.rs b/src/tools/clippy/clippy_lints/src/unnecessary_map_on_constructor.rs
index 5aa057580..9107b2b99 100644
--- a/src/tools/clippy/clippy_lints/src/unnecessary_map_on_constructor.rs
+++ b/src/tools/clippy/clippy_lints/src/unnecessary_map_on_constructor.rs
@@ -9,18 +9,18 @@ use rustc_span::sym;
declare_clippy_lint! {
/// ### What it does
- /// Suggest removing the use of a may (or map_err) method when an Option or Result is being construted.
+ /// Suggest removing the use of a may (or map_err) method when an Option or Result is being constructed.
///
/// ### Why is this bad?
/// It introduces unnecessary complexity. In this case the function can be used directly and
/// construct the Option or Result from the output.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// Some(4).map(i32::swap_bytes);
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// Some(i32::swap_bytes(4));
/// ```
#[clippy::version = "1.73.0"]
@@ -36,19 +36,20 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryMapOnConstructor {
return;
}
if let hir::ExprKind::MethodCall(path, recv, args, ..) = expr.kind
- && let Some(sym::Option | sym::Result) = get_type_diagnostic_name(cx, cx.typeck_results().expr_ty(recv)){
- let (constructor_path, constructor_item) =
- if let hir::ExprKind::Call(constructor, constructor_args) = recv.kind
- && let hir::ExprKind::Path(constructor_path) = constructor.kind
- && let Some(arg) = constructor_args.get(0)
- {
- if constructor.span.from_expansion() || arg.span.from_expansion() {
- return;
- }
- (constructor_path, arg)
- } else {
+ && let Some(sym::Option | sym::Result) = get_type_diagnostic_name(cx, cx.typeck_results().expr_ty(recv))
+ {
+ let (constructor_path, constructor_item) = if let hir::ExprKind::Call(constructor, constructor_args) =
+ recv.kind
+ && let hir::ExprKind::Path(constructor_path) = constructor.kind
+ && let Some(arg) = constructor_args.first()
+ {
+ if constructor.span.from_expansion() || arg.span.from_expansion() {
return;
- };
+ }
+ (constructor_path, arg)
+ } else {
+ return;
+ };
let constructor_symbol = match constructor_path {
hir::QPath::Resolved(_, path) => {
if let Some(path_segment) = path.segments.last() {
@@ -66,7 +67,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryMapOnConstructor {
_ => return,
}
- if let Some(map_arg) = args.get(0)
+ if let Some(map_arg) = args.first()
&& let hir::ExprKind::Path(fun) = map_arg.kind
{
if map_arg.span.from_expansion() {
@@ -82,7 +83,10 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryMapOnConstructor {
cx,
UNNECESSARY_MAP_ON_CONSTRUCTOR,
expr.span,
- &format!("unnecessary {} on constructor {constructor_snippet}(_)", path.ident.name),
+ &format!(
+ "unnecessary {} on constructor {constructor_snippet}(_)",
+ path.ident.name
+ ),
"try",
format!("{constructor_snippet}({fun_snippet}({constructor_arg_snippet}))"),
applicability,
diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_owned_empty_strings.rs b/src/tools/clippy/clippy_lints/src/unnecessary_owned_empty_strings.rs
index 57a4a429e..28ea02e4d 100644
--- a/src/tools/clippy/clippy_lints/src/unnecessary_owned_empty_strings.rs
+++ b/src/tools/clippy/clippy_lints/src/unnecessary_owned_empty_strings.rs
@@ -20,11 +20,11 @@ declare_clippy_lint! {
/// This results in longer and less readable code
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// vec!["1", "2", "3"].join(&String::new());
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// vec!["1", "2", "3"].join("");
/// ```
#[clippy::version = "1.62.0"]
diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_self_imports.rs b/src/tools/clippy/clippy_lints/src/unnecessary_self_imports.rs
index 397633f53..a1083a0a6 100644
--- a/src/tools/clippy/clippy_lints/src/unnecessary_self_imports.rs
+++ b/src/tools/clippy/clippy_lints/src/unnecessary_self_imports.rs
@@ -19,11 +19,11 @@ declare_clippy_lint! {
/// to detect this scenario and that is why it is a restriction lint.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// use std::io::{self};
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// use std::io;
/// ```
#[clippy::version = "1.53.0"]
diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_struct_initialization.rs b/src/tools/clippy/clippy_lints/src/unnecessary_struct_initialization.rs
index f4111186c..c35a2afab 100644
--- a/src/tools/clippy/clippy_lints/src/unnecessary_struct_initialization.rs
+++ b/src/tools/clippy/clippy_lints/src/unnecessary_struct_initialization.rs
@@ -15,14 +15,14 @@ declare_clippy_lint! {
/// Readability suffers from unnecessary struct building.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// struct S { s: String }
///
/// let a = S { s: String::from("Hello, world!") };
/// let b = S { ..a };
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// struct S { s: String }
///
/// let a = S { s: String::from("Hello, world!") };
@@ -42,9 +42,9 @@ declare_lint_pass!(UnnecessaryStruct => [UNNECESSARY_STRUCT_INITIALIZATION]);
impl LateLintPass<'_> for UnnecessaryStruct {
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
if let ExprKind::Struct(_, &[], Some(base)) = expr.kind {
- if let Some(parent) = get_parent_expr(cx, expr) &&
- let parent_ty = cx.typeck_results().expr_ty_adjusted(parent) &&
- parent_ty.is_any_ptr()
+ if let Some(parent) = get_parent_expr(cx, expr)
+ && let parent_ty = cx.typeck_results().expr_ty_adjusted(parent)
+ && parent_ty.is_any_ptr()
{
if is_copy(cx, cx.typeck_results().expr_ty(expr)) && path_to_local(base).is_some() {
// When the type implements `Copy`, a reference to the new struct works on the
@@ -59,9 +59,9 @@ impl LateLintPass<'_> for UnnecessaryStruct {
}
// TODO: do not propose to replace *XX if XX is not Copy
- if let ExprKind::Unary(UnOp::Deref, target) = base.kind &&
- matches!(target.kind, ExprKind::Path(..)) &&
- !is_copy(cx, cx.typeck_results().expr_ty(expr))
+ if let ExprKind::Unary(UnOp::Deref, target) = base.kind
+ && matches!(target.kind, ExprKind::Path(..))
+ && !is_copy(cx, cx.typeck_results().expr_ty(expr))
{
// `*base` cannot be used instead of the struct in the general case if it is not Copy.
return;
@@ -81,8 +81,8 @@ impl LateLintPass<'_> for UnnecessaryStruct {
}
fn is_mutable(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
- if let Some(hir_id) = path_to_local(expr) &&
- let Node::Pat(pat) = cx.tcx.hir().get(hir_id)
+ if let Some(hir_id) = path_to_local(expr)
+ && let Node::Pat(pat) = cx.tcx.hir().get(hir_id)
{
matches!(pat.kind, PatKind::Binding(BindingAnnotation::MUT, ..))
} else {
diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs b/src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs
index f34f8d0e3..ab8de17b0 100644
--- a/src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs
+++ b/src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs
@@ -26,7 +26,7 @@ declare_clippy_lint! {
/// fit some external requirement.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// fn get_cool_number(a: bool, b: bool) -> Option<i32> {
/// if a && b {
/// return Some(50);
@@ -39,7 +39,7 @@ declare_clippy_lint! {
/// }
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// fn get_cool_number(a: bool, b: bool) -> i32 {
/// if a && b {
/// return 50;
diff --git a/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs b/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs
index 766a54814..8ff088a20 100644
--- a/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs
+++ b/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs
@@ -1,8 +1,8 @@
#![allow(clippy::wildcard_imports, clippy::enum_glob_use)]
+use clippy_config::msrvs::{self, Msrv};
use clippy_utils::ast_utils::{eq_field_pat, eq_id, eq_maybe_qself, eq_pat, eq_path};
use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::over;
use rustc_ast::mut_visit::*;
use rustc_ast::ptr::P;
@@ -29,13 +29,13 @@ declare_clippy_lint! {
/// In the example above, `Some` is repeated, which unnecessarily complicates the pattern.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// fn main() {
/// if let Some(0) | Some(2) = Some(0) {}
/// }
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// fn main() {
/// if let Some(0 | 2) = Some(0) {}
/// }
diff --git a/src/tools/clippy/clippy_lints/src/unsafe_removed_from_name.rs b/src/tools/clippy/clippy_lints/src/unsafe_removed_from_name.rs
index 7ee785804..c43d5dc94 100644
--- a/src/tools/clippy/clippy_lints/src/unsafe_removed_from_name.rs
+++ b/src/tools/clippy/clippy_lints/src/unsafe_removed_from_name.rs
@@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint;
use rustc_ast::ast::{Item, ItemKind, UseTree, UseTreeKind};
use rustc_lint::{EarlyContext, EarlyLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::source_map::Span;
+use rustc_span::Span;
use rustc_span::symbol::Ident;
declare_clippy_lint! {
diff --git a/src/tools/clippy/clippy_lints/src/unused_async.rs b/src/tools/clippy/clippy_lints/src/unused_async.rs
index bc7c3897a..aea72c798 100644
--- a/src/tools/clippy/clippy_lints/src/unused_async.rs
+++ b/src/tools/clippy/clippy_lints/src/unused_async.rs
@@ -19,7 +19,7 @@ declare_clippy_lint! {
/// causes runtime overhead and hassle for the caller.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// async fn get_random_number() -> i64 {
/// 4 // Chosen by fair dice roll. Guaranteed to be random.
/// }
@@ -27,7 +27,7 @@ declare_clippy_lint! {
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// fn get_random_number_improved() -> i64 {
/// 4 // Chosen by fair dice roll. Guaranteed to be random.
/// }
@@ -86,7 +86,7 @@ impl<'a, 'tcx> Visitor<'tcx> for AsyncFnVisitor<'a, 'tcx> {
}
fn visit_body(&mut self, b: &'tcx Body<'tcx>) {
- let is_async_block = matches!(b.generator_kind, Some(rustc_hir::GeneratorKind::Async(_)));
+ let is_async_block = matches!(b.coroutine_kind, Some(rustc_hir::CoroutineKind::Async(_)));
if is_async_block {
self.async_depth += 1;
diff --git a/src/tools/clippy/clippy_lints/src/unused_peekable.rs b/src/tools/clippy/clippy_lints/src/unused_peekable.rs
index 4ee16d9a5..0473ecaab 100644
--- a/src/tools/clippy/clippy_lints/src/unused_peekable.rs
+++ b/src/tools/clippy/clippy_lints/src/unused_peekable.rs
@@ -1,6 +1,6 @@
use clippy_utils::diagnostics::span_lint_and_help;
-use clippy_utils::ty::{match_type, peel_mid_ty_refs_is_mutable};
-use clippy_utils::{fn_def_id, is_trait_method, path_to_local_id, paths, peel_ref_operators};
+use clippy_utils::ty::{is_type_diagnostic_item, peel_mid_ty_refs_is_mutable};
+use clippy_utils::{fn_def_id, is_trait_method, path_to_local_id, peel_ref_operators};
use rustc_ast::Mutability;
use rustc_hir::intravisit::{walk_expr, Visitor};
use rustc_hir::{Block, Expr, ExprKind, HirId, Local, Node, PatKind, PathSegment, StmtKind};
@@ -18,7 +18,7 @@ declare_clippy_lint! {
/// or just a leftover after a refactor.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let collection = vec![1, 2, 3];
/// let iter = collection.iter().peekable();
///
@@ -28,7 +28,7 @@ declare_clippy_lint! {
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// let collection = vec![1, 2, 3];
/// let iter = collection.iter();
///
@@ -49,7 +49,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedPeekable {
// Don't lint `Peekable`s returned from a block
if let Some(expr) = block.expr
&& let Some(ty) = cx.typeck_results().expr_ty_opt(peel_ref_operators(cx, expr))
- && match_type(cx, ty, &paths::PEEKABLE)
+ && is_type_diagnostic_item(cx, ty, sym::IterPeekable)
{
return;
}
@@ -62,7 +62,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedPeekable {
&& !init.span.from_expansion()
&& let Some(ty) = cx.typeck_results().expr_ty_opt(init)
&& let (ty, _, Mutability::Mut) = peel_mid_ty_refs_is_mutable(ty)
- && match_type(cx, ty, &paths::PEEKABLE)
+ && is_type_diagnostic_item(cx, ty, sym::IterPeekable)
{
let mut vis = PeekableVisitor::new(cx, binding);
@@ -85,8 +85,8 @@ impl<'tcx> LateLintPass<'tcx> for UnusedPeekable {
ident.span,
"`peek` never called on `Peekable` iterator",
None,
- "consider removing the call to `peekable`"
- );
+ "consider removing the call to `peekable`",
+ );
}
}
}
@@ -131,11 +131,7 @@ impl<'tcx> Visitor<'tcx> for PeekableVisitor<'_, 'tcx> {
// If the Peekable is passed to a function, stop
ExprKind::Call(_, args) => {
if let Some(func_did) = fn_def_id(self.cx, expr)
- && let Some(into_iter_did) = self
- .cx
- .tcx
- .lang_items()
- .into_iter_fn()
+ && let Some(into_iter_did) = self.cx.tcx.lang_items().into_iter_fn()
&& func_did == into_iter_did
{
// Probably a for loop desugar, stop searching
@@ -222,7 +218,7 @@ impl<'tcx> Visitor<'tcx> for PeekableVisitor<'_, 'tcx> {
fn arg_is_mut_peekable(cx: &LateContext<'_>, arg: &Expr<'_>) -> bool {
if let Some(ty) = cx.typeck_results().expr_ty_opt(arg)
&& let (ty, _, Mutability::Mut) = peel_mid_ty_refs_is_mutable(ty)
- && match_type(cx, ty, &paths::PEEKABLE)
+ && is_type_diagnostic_item(cx, ty, sym::IterPeekable)
{
true
} else {
diff --git a/src/tools/clippy/clippy_lints/src/unused_rounding.rs b/src/tools/clippy/clippy_lints/src/unused_rounding.rs
index 097568cd1..fbb36bea0 100644
--- a/src/tools/clippy/clippy_lints/src/unused_rounding.rs
+++ b/src/tools/clippy/clippy_lints/src/unused_rounding.rs
@@ -16,11 +16,11 @@ declare_clippy_lint! {
/// This is unnecessary and confusing to the reader. Doing this is probably a mistake.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let x = 1f32.ceil();
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// let x = 1f32;
/// ```
#[clippy::version = "1.63.0"]
@@ -31,18 +31,21 @@ declare_clippy_lint! {
declare_lint_pass!(UnusedRounding => [UNUSED_ROUNDING]);
fn is_useless_rounding<'a>(cx: &EarlyContext<'_>, expr: &'a Expr) -> Option<(&'a str, String)> {
- if let ExprKind::MethodCall(box MethodCall { seg:name_ident, receiver, .. }) = &expr.kind
+ if let ExprKind::MethodCall(box MethodCall {
+ seg: name_ident,
+ receiver,
+ ..
+ }) = &expr.kind
&& let method_name = name_ident.ident.name.as_str()
&& (method_name == "ceil" || method_name == "round" || method_name == "floor")
&& let ExprKind::Lit(token_lit) = &receiver.kind
&& token_lit.is_semantic_float()
- && let Ok(f) = token_lit.symbol.as_str().replace('_', "").parse::<f64>() {
- (f.fract() == 0.0).then(||
- (method_name, snippet(cx, receiver.span, "..").to_string())
- )
- } else {
- None
- }
+ && let Ok(f) = token_lit.symbol.as_str().replace('_', "").parse::<f64>()
+ {
+ (f.fract() == 0.0).then(|| (method_name, snippet(cx, receiver.span, "..").to_string()))
+ } else {
+ None
+ }
}
impl EarlyLintPass for UnusedRounding {
diff --git a/src/tools/clippy/clippy_lints/src/unused_unit.rs b/src/tools/clippy/clippy_lints/src/unused_unit.rs
index 95e74718d..adbf82813 100644
--- a/src/tools/clippy/clippy_lints/src/unused_unit.rs
+++ b/src/tools/clippy/clippy_lints/src/unused_unit.rs
@@ -6,8 +6,7 @@ use rustc_ast::{ast, ClosureBinder};
use rustc_errors::Applicability;
use rustc_lint::{EarlyContext, EarlyLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::source_map::Span;
-use rustc_span::BytePos;
+use rustc_span::{BytePos, Span};
declare_clippy_lint! {
/// ### What it does
@@ -19,13 +18,13 @@ declare_clippy_lint! {
/// statement look like a function call.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// fn return_unit() -> () {
/// ()
/// }
/// ```
/// is equivalent to
- /// ```rust
+ /// ```no_run
/// fn return_unit() {}
/// ```
#[clippy::version = "1.31.0"]
diff --git a/src/tools/clippy/clippy_lints/src/unwrap.rs b/src/tools/clippy/clippy_lints/src/unwrap.rs
index 9a0d83d83..cdfcb8500 100644
--- a/src/tools/clippy/clippy_lints/src/unwrap.rs
+++ b/src/tools/clippy/clippy_lints/src/unwrap.rs
@@ -15,8 +15,7 @@ use rustc_middle::mir::FakeReadCause;
use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::def_id::LocalDefId;
-use rustc_span::source_map::Span;
-use rustc_span::sym;
+use rustc_span::{sym, Span};
declare_clippy_lint! {
/// ### What it does
@@ -26,7 +25,7 @@ declare_clippy_lint! {
/// Using `if let` or `match` is more idiomatic.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # let option = Some(0);
/// # fn do_something_with(_x: usize) {}
/// if option.is_some() {
@@ -36,7 +35,7 @@ declare_clippy_lint! {
///
/// Could be written:
///
- /// ```rust
+ /// ```no_run
/// # let option = Some(0);
/// # fn do_something_with(_x: usize) {}
/// if let Some(value) = option {
@@ -61,7 +60,7 @@ declare_clippy_lint! {
/// So something like `let x: Option<()> = None; x.unwrap();` will not be recognized.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # let option = Some(0);
/// # fn do_something_with(_x: usize) {}
/// if option.is_none() {
diff --git a/src/tools/clippy/clippy_lints/src/unwrap_in_result.rs b/src/tools/clippy/clippy_lints/src/unwrap_in_result.rs
index 3a1845425..21592abbf 100644
--- a/src/tools/clippy/clippy_lints/src/unwrap_in_result.rs
+++ b/src/tools/clippy/clippy_lints/src/unwrap_in_result.rs
@@ -22,7 +22,7 @@ declare_clippy_lint! {
///
/// ### Example
/// Before:
- /// ```rust
+ /// ```no_run
/// fn divisible_by_3(i_str: String) -> Result<(), String> {
/// let i = i_str
/// .parse::<i32>()
@@ -37,7 +37,7 @@ declare_clippy_lint! {
/// ```
///
/// After:
- /// ```rust
+ /// ```no_run
/// fn divisible_by_3(i_str: String) -> Result<(), String> {
/// let i = i_str
/// .parse::<i32>()
diff --git a/src/tools/clippy/clippy_lints/src/upper_case_acronyms.rs b/src/tools/clippy/clippy_lints/src/upper_case_acronyms.rs
index 4df1e3299..de6a75b79 100644
--- a/src/tools/clippy/clippy_lints/src/upper_case_acronyms.rs
+++ b/src/tools/clippy/clippy_lints/src/upper_case_acronyms.rs
@@ -26,11 +26,11 @@ declare_clippy_lint! {
/// the letters in the second acronym.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// struct HTTPResponse;
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// struct HttpResponse;
/// ```
#[clippy::version = "1.51.0"]
diff --git a/src/tools/clippy/clippy_lints/src/use_self.rs b/src/tools/clippy/clippy_lints/src/use_self.rs
index 50231d930..c3fe16ad5 100644
--- a/src/tools/clippy/clippy_lints/src/use_self.rs
+++ b/src/tools/clippy/clippy_lints/src/use_self.rs
@@ -1,6 +1,6 @@
+use clippy_config::msrvs::{self, Msrv};
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::is_from_proc_macro;
-use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::ty::same_type_and_consts;
use if_chain::if_chain;
use rustc_data_structures::fx::FxHashSet;
@@ -31,7 +31,7 @@ declare_clippy_lint! {
/// - Unaddressed false negative in fn bodies of trait implementations
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// struct Foo;
/// impl Foo {
/// fn new() -> Foo {
@@ -40,7 +40,7 @@ declare_clippy_lint! {
/// }
/// ```
/// could be
- /// ```rust
+ /// ```no_run
/// struct Foo;
/// impl Foo {
/// fn new() -> Self {
@@ -54,7 +54,6 @@ declare_clippy_lint! {
"unnecessary structure name repetition whereas `Self` is applicable"
}
-#[derive(Default)]
pub struct UseSelf {
msrv: Msrv,
stack: Vec<StackItem>,
@@ -65,7 +64,7 @@ impl UseSelf {
pub fn new(msrv: Msrv) -> Self {
Self {
msrv,
- ..Self::default()
+ stack: Vec::new(),
}
}
}
diff --git a/src/tools/clippy/clippy_lints/src/useless_conversion.rs b/src/tools/clippy/clippy_lints/src/useless_conversion.rs
index f32e7edad..28f1d487e 100644
--- a/src/tools/clippy/clippy_lints/src/useless_conversion.rs
+++ b/src/tools/clippy/clippy_lints/src/useless_conversion.rs
@@ -2,7 +2,7 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg, span_lin
use clippy_utils::source::{snippet, snippet_with_applicability, snippet_with_context};
use clippy_utils::sugg::Sugg;
use clippy_utils::ty::{is_copy, is_type_diagnostic_item, same_type_and_consts};
-use clippy_utils::{get_parent_expr, is_trait_method, is_ty_alias, match_def_path, path_to_local, paths};
+use clippy_utils::{get_parent_expr, is_trait_method, is_ty_alias, path_to_local};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::def::DefKind;
@@ -26,13 +26,13 @@ declare_clippy_lint! {
/// Redundant code.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// // format!() returns a `String`
/// let s: String = format!("hello").into();
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// let s: String = format!("hello");
/// ```
#[clippy::version = "1.45.0"]
@@ -215,20 +215,19 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion {
did,
args,
cx.typeck_results().node_args(recv.hir_id),
- MethodOrFunction::Function
+ MethodOrFunction::Function,
))
- }
+ },
ExprKind::MethodCall(.., args, _) => {
- cx.typeck_results().type_dependent_def_id(parent.hir_id)
- .map(|did| {
- return (
- did,
- args,
- cx.typeck_results().node_args(parent.hir_id),
- MethodOrFunction::Method
- );
- })
- }
+ cx.typeck_results().type_dependent_def_id(parent.hir_id).map(|did| {
+ return (
+ did,
+ args,
+ cx.typeck_results().node_args(parent.hir_id),
+ MethodOrFunction::Method,
+ );
+ })
+ },
_ => None,
};
@@ -244,7 +243,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion {
into_iter_did,
cx.typeck_results().expr_ty(into_iter_recv),
param.index,
- node_args
+ node_args,
)
&& self.expn_depth == 0
{
@@ -255,26 +254,38 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion {
let plural = if depth == 0 { "" } else { "s" };
let mut applicability = Applicability::MachineApplicable;
- let sugg = snippet_with_applicability(cx, into_iter_recv.span.source_callsite(), "<expr>", &mut applicability).into_owned();
- span_lint_and_then(cx, USELESS_CONVERSION, e.span, "explicit call to `.into_iter()` in function argument accepting `IntoIterator`", |diag| {
- diag.span_suggestion(
- e.span,
- format!("consider removing the `.into_iter()`{plural}"),
- sugg,
- applicability,
- );
- diag.span_note(span, "this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()`");
- });
+ let sugg = snippet_with_applicability(
+ cx,
+ into_iter_recv.span.source_callsite(),
+ "<expr>",
+ &mut applicability,
+ )
+ .into_owned();
+ span_lint_and_then(
+ cx,
+ USELESS_CONVERSION,
+ e.span,
+ "explicit call to `.into_iter()` in function argument accepting `IntoIterator`",
+ |diag| {
+ diag.span_suggestion(
+ e.span,
+ format!("consider removing the `.into_iter()`{plural}"),
+ sugg,
+ applicability,
+ );
+ diag.span_note(span, "this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()`");
+ },
+ );
// Early return to avoid linting again with contradicting suggestions
return;
}
}
- if let Some(id) = path_to_local(recv) &&
- let Node::Pat(pat) = cx.tcx.hir().get(id) &&
- let PatKind::Binding(ann, ..) = pat.kind &&
- ann != BindingAnnotation::MUT
+ if let Some(id) = path_to_local(recv)
+ && let Node::Pat(pat) = cx.tcx.hir().get(id)
+ && let PatKind::Binding(ann, ..) = pat.kind
+ && ann != BindingAnnotation::MUT
{
// Do not remove .into_iter() applied to a non-mutable local variable used in
// a larger expression context as it would differ in mutability.
@@ -331,7 +342,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion {
let a = cx.typeck_results().expr_ty(e);
let b = cx.typeck_results().expr_ty(arg);
if_chain! {
- if match_def_path(cx, def_id, &paths::TRY_FROM);
+ if cx.tcx.is_diagnostic_item(sym::try_from_fn, def_id);
if is_type_diagnostic_item(cx, a, sym::Result);
if let ty::Adt(_, args) = a.kind();
if let Some(a_type) = args.types().next();
diff --git a/src/tools/clippy/clippy_lints/src/utils/author.rs b/src/tools/clippy/clippy_lints/src/utils/author.rs
index f02c33cc6..152248afc 100644
--- a/src/tools/clippy/clippy_lints/src/utils/author.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/author.rs
@@ -7,15 +7,15 @@ use rustc_ast::LitIntType;
use rustc_data_structures::fx::FxHashMap;
use rustc_hir as hir;
use rustc_hir::{
- ArrayLen, BindingAnnotation, Closure, ExprKind, FnRetTy, HirId, Lit, PatKind, QPath, StmtKind, TyKind,
+ ArrayLen, BindingAnnotation, Closure, ExprKind, FnRetTy, HirId, Lit, PatKind, QPath, StmtKind, TyKind, CaptureBy
};
use rustc_lint::{LateContext, LateLintPass, LintContext};
-use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_session::declare_lint_pass;
use rustc_span::symbol::{Ident, Symbol};
use std::cell::Cell;
use std::fmt::{Display, Formatter, Write as _};
-declare_clippy_lint! {
+declare_lint_pass!(
/// ### What it does
/// Generates clippy code that detects the offending pattern
///
@@ -47,12 +47,8 @@ declare_clippy_lint! {
/// // report your lint here
/// }
/// ```
- pub LINT_AUTHOR,
- internal_warn,
- "helper for writing lints"
-}
-
-declare_lint_pass!(Author => [LINT_AUTHOR]);
+ Author => []
+);
/// Writes a line of output with indentation added
macro_rules! out {
@@ -268,8 +264,8 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
fn qpath(&self, qpath: &Binding<&QPath<'_>>) {
if let QPath::LangItem(lang_item, ..) = *qpath.value {
chain!(self, "matches!({qpath}, QPath::LangItem(LangItem::{lang_item:?}, _))");
- } else {
- chain!(self, "match_qpath({qpath}, &[{}])", path_to_string(qpath.value));
+ } else if let Ok(path) = path_to_string(qpath.value) {
+ chain!(self, "match_qpath({qpath}, &[{}])", path);
}
}
@@ -483,6 +479,11 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
movability,
..
}) => {
+ let capture_clause = match capture_clause {
+ CaptureBy::Value { .. } => "Value { .. }",
+ CaptureBy::Ref => "Ref",
+ };
+
let movability = OptionPat::new(movability.map(|m| format!("Movability::{m:?}")));
let ret_ty = match fn_decl.output {
@@ -491,7 +492,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
};
bind!(self, fn_decl, body_id);
- kind!("Closure(CaptureBy::{capture_clause:?}, {fn_decl}, {body_id}, _, {movability})");
+ kind!("Closure(CaptureBy::{capture_clause}, {fn_decl}, {body_id}, _, {movability})");
chain!(self, "let {ret_ty} = {fn_decl}.output");
self.body(body_id);
},
@@ -738,8 +739,8 @@ fn has_attr(cx: &LateContext<'_>, hir_id: hir::HirId) -> bool {
get_attr(cx.sess(), attrs, "author").count() > 0
}
-fn path_to_string(path: &QPath<'_>) -> String {
- fn inner(s: &mut String, path: &QPath<'_>) {
+fn path_to_string(path: &QPath<'_>) -> Result<String, ()> {
+ fn inner(s: &mut String, path: &QPath<'_>) -> Result<(), ()> {
match *path {
QPath::Resolved(_, path) => {
for (i, segment) in path.segments.iter().enumerate() {
@@ -751,16 +752,18 @@ fn path_to_string(path: &QPath<'_>) -> String {
},
QPath::TypeRelative(ty, segment) => match &ty.kind {
hir::TyKind::Path(inner_path) => {
- inner(s, inner_path);
+ inner(s, inner_path)?;
*s += ", ";
write!(s, "{:?}", segment.ident.as_str()).unwrap();
},
other => write!(s, "/* unimplemented: {other:?}*/").unwrap(),
},
- QPath::LangItem(..) => panic!("path_to_string: called for lang item qpath"),
+ QPath::LangItem(..) => return Err(()),
}
+
+ Ok(())
}
let mut s = String::new();
- inner(&mut s, path);
- s
+ inner(&mut s, path)?;
+ Ok(s)
}
diff --git a/src/tools/clippy/clippy_lints/src/utils/dump_hir.rs b/src/tools/clippy/clippy_lints/src/utils/dump_hir.rs
index 092041aec..b10895197 100644
--- a/src/tools/clippy/clippy_lints/src/utils/dump_hir.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/dump_hir.rs
@@ -2,9 +2,9 @@ use clippy_utils::get_attr;
use hir::TraitItem;
use rustc_hir as hir;
use rustc_lint::{LateContext, LateLintPass, LintContext};
-use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_session::declare_lint_pass;
-declare_clippy_lint! {
+declare_lint_pass!(
/// ### What it does
/// It formats the attached node with `{:#?}` and writes the result to the
/// standard output. This is intended for debugging.
@@ -19,12 +19,8 @@ declare_clippy_lint! {
/// input as u64
/// }
/// ```
- pub DUMP_HIR,
- internal_warn,
- "helper to dump info about code"
-}
-
-declare_lint_pass!(DumpHir => [DUMP_HIR]);
+ DumpHir => []
+);
impl<'tcx> LateLintPass<'tcx> for DumpHir {
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
diff --git a/src/tools/clippy/clippy_lints/src/utils/format_args_collector.rs b/src/tools/clippy/clippy_lints/src/utils/format_args_collector.rs
index 94a9a7c24..58e66c9f9 100644
--- a/src/tools/clippy/clippy_lints/src/utils/format_args_collector.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/format_args_collector.rs
@@ -5,27 +5,20 @@ use rustc_ast::{Crate, Expr, ExprKind, FormatArgs};
use rustc_data_structures::fx::FxHashMap;
use rustc_lexer::{tokenize, TokenKind};
use rustc_lint::{EarlyContext, EarlyLintPass};
-use rustc_session::{declare_tool_lint, impl_lint_pass};
+use rustc_session::impl_lint_pass;
use rustc_span::{hygiene, Span};
use std::iter::once;
use std::mem;
use std::rc::Rc;
-declare_clippy_lint! {
- /// ### What it does
- /// Collects [`rustc_ast::FormatArgs`] so that future late passes can call
- /// [`clippy_utils::macros::find_format_args`]
- pub FORMAT_ARGS_COLLECTOR,
- internal_warn,
- "collects `format_args` AST nodes for use in later lints"
-}
-
+/// Collects [`rustc_ast::FormatArgs`] so that future late passes can call
+/// [`clippy_utils::macros::find_format_args`]
#[derive(Default)]
pub struct FormatArgsCollector {
format_args: FxHashMap<Span, Rc<FormatArgs>>,
}
-impl_lint_pass!(FormatArgsCollector => [FORMAT_ARGS_COLLECTOR]);
+impl_lint_pass!(FormatArgsCollector => []);
impl EarlyLintPass for FormatArgsCollector {
fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs
index e222a5448..ddcb9f27c 100644
--- a/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs
@@ -1,5 +1,4 @@
pub mod almost_standard_lint_formulation;
-pub mod clippy_lints_internal;
pub mod collapsible_calls;
pub mod compiler_lint_functions;
pub mod if_chain_style;
@@ -11,3 +10,4 @@ pub mod msrv_attr_impl;
pub mod outer_expn_data_pass;
pub mod produce_ice;
pub mod unnecessary_def_path;
+pub mod unsorted_clippy_utils_paths;
diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/almost_standard_lint_formulation.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/almost_standard_lint_formulation.rs
index 570a88a0e..d78f67c05 100644
--- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/almost_standard_lint_formulation.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints/almost_standard_lint_formulation.rs
@@ -11,7 +11,7 @@ declare_clippy_lint! {
/// Checks if lint formulations have a standardized format.
///
/// ### Why is this bad?
- /// It's not neccessarily bad, but we try to enforce a standard in Clippy.
+ /// It's not necessarily bad, but we try to enforce a standard in Clippy.
///
/// ### Example
/// `Checks for use...` can be written as `Checks for usage...` .
diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/if_chain_style.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/if_chain_style.rs
index fe2f12fe8..8cdd5ea89 100644
--- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/if_chain_style.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints/if_chain_style.rs
@@ -30,7 +30,7 @@ impl<'tcx> LateLintPass<'tcx> for IfChainStyle {
if_chain_local_span(cx, local, if_chain_span),
"`let` expression should be above the `if_chain!`",
);
- } else if local.span.ctxt() == block.span.ctxt() && is_if_chain_then(after, block.expr, if_chain_span) {
+ } else if local.span.eq_ctxt(block.span) && is_if_chain_then(after, block.expr, if_chain_span) {
span_lint(
cx,
IF_CHAIN_STYLE,
diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs
index 82f9d4e41..fc9afe5ca 100644
--- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs
@@ -13,6 +13,7 @@ use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::mir::ConstValue;
use rustc_middle::ty;
use rustc_session::{declare_tool_lint, impl_lint_pass};
+use rustc_span::sym;
use rustc_span::symbol::Symbol;
use std::borrow::Cow;
@@ -160,12 +161,8 @@ impl<'tcx> LateLintPass<'tcx> for InterningDefinedSymbol {
impl InterningDefinedSymbol {
fn symbol_str_expr<'tcx>(&self, expr: &'tcx Expr<'tcx>, cx: &LateContext<'tcx>) -> Option<SymbolStrExpr<'tcx>> {
- static IDENT_STR_PATHS: &[&[&str]] = &[&paths::IDENT_AS_STR, &paths::TO_STRING_METHOD];
- static SYMBOL_STR_PATHS: &[&[&str]] = &[
- &paths::SYMBOL_AS_STR,
- &paths::SYMBOL_TO_IDENT_STRING,
- &paths::TO_STRING_METHOD,
- ];
+ static IDENT_STR_PATHS: &[&[&str]] = &[&paths::IDENT_AS_STR];
+ static SYMBOL_STR_PATHS: &[&[&str]] = &[&paths::SYMBOL_AS_STR, &paths::SYMBOL_TO_IDENT_STRING];
let call = if_chain! {
if let ExprKind::AddrOf(_, _, e) = expr.kind;
if let ExprKind::Unary(UnOp::Deref, e) = e.kind;
@@ -186,9 +183,19 @@ impl InterningDefinedSymbol {
};
// ...which converts it to a string
let paths = if is_ident { IDENT_STR_PATHS } else { SYMBOL_STR_PATHS };
- if let Some(path) = paths.iter().find(|path| match_def_path(cx, did, path));
+ if let Some(is_to_owned) = paths
+ .iter()
+ .find_map(|path| if match_def_path(cx, did, path) {
+ Some(path == &paths::SYMBOL_TO_IDENT_STRING)
+ } else {
+ None
+ })
+ .or_else(|| if cx.tcx.is_diagnostic_item(sym::to_string_method, did) {
+ Some(true)
+ } else {
+ None
+ });
then {
- let is_to_owned = path.last().unwrap().ends_with("string");
return Some(SymbolStrExpr::Expr {
item,
is_ident,
diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs
index bbb5ade8b..00e352961 100644
--- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs
@@ -153,8 +153,9 @@ impl<'tcx> LateLintPass<'tcx> for LintWithoutLintPass {
let fields;
if is_lint_ref_ty {
if let ExprKind::AddrOf(_, _, inner_exp) = expr.kind
- && let ExprKind::Struct(_, struct_fields, _) = inner_exp.kind {
- fields = struct_fields;
+ && let ExprKind::Struct(_, struct_fields, _) = inner_exp.kind
+ {
+ fields = struct_fields;
} else {
return;
}
diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs
index c38a3e81b..51abe0c1d 100644
--- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs
@@ -9,7 +9,7 @@
use crate::renamed_lints::RENAMED_LINTS;
use crate::utils::internal_lints::lint_without_lint_pass::{extract_clippy_version_value, is_lint_ref_type};
-use crate::utils::{collect_configs, ClippyConfiguration};
+use clippy_config::{get_configuration_metadata, ClippyConfiguration};
use clippy_utils::diagnostics::span_lint;
use clippy_utils::ty::{match_type, walk_ptrs_ty_depth};
@@ -40,8 +40,6 @@ use std::process::Command;
const JSON_OUTPUT_FILE: &str = "../util/gh-pages/lints.json";
/// This is the markdown output file of the lint collector.
const MARKDOWN_OUTPUT_FILE: &str = "../book/src/lint_configuration.md";
-/// These lints are excluded from the export.
-const BLACK_LISTED_LINTS: &[&str] = &["lint_author", "dump_hir", "internal_metadata_collector"];
/// These groups will be ignored by the lint group matcher. This is useful for collections like
/// `clippy::all`
const IGNORED_LINT_GROUPS: [&str; 1] = ["clippy::all"];
@@ -121,7 +119,7 @@ declare_clippy_lint! {
/// ### Example output
/// ```json,ignore
/// {
- /// "id": "internal_metadata_collector",
+ /// "id": "metadata_collector",
/// "id_span": {
/// "path": "clippy_lints/src/utils/internal_lints/metadata_collector.rs",
/// "line": 1
@@ -131,12 +129,12 @@ declare_clippy_lint! {
/// }
/// ```
#[clippy::version = "1.56.0"]
- pub INTERNAL_METADATA_COLLECTOR,
- internal_warn,
+ pub METADATA_COLLECTOR,
+ internal,
"A busy bee collection metadata about lints"
}
-impl_lint_pass!(MetadataCollector => [INTERNAL_METADATA_COLLECTOR]);
+impl_lint_pass!(MetadataCollector => [METADATA_COLLECTOR]);
#[allow(clippy::module_name_repetitions)]
#[derive(Debug, Clone)]
@@ -155,7 +153,7 @@ impl MetadataCollector {
Self {
lints: BinaryHeap::<LintMetadata>::default(),
applicability_info: FxHashMap::<String, ApplicabilityInfo>::default(),
- config: collect_configs(),
+ config: get_configuration_metadata(),
clippy_project_root: std::env::current_dir()
.expect("failed to get current dir")
.ancestors()
@@ -494,7 +492,7 @@ impl SerializableSpan {
let loc: Loc = cx.sess().source_map().lookup_char_pos(span.lo());
Self {
- path: format!("{}", loc.file.name.prefer_remapped()),
+ path: format!("{}", loc.file.name.prefer_remapped_unconditionaly()),
line: loc.line,
}
}
@@ -528,16 +526,6 @@ impl Serialize for ApplicabilityInfo {
}
}
-impl fmt::Display for ClippyConfiguration {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> fmt::Result {
- writeln!(
- f,
- "* `{}`: `{}`(defaults to `{}`): {}",
- self.name, self.config_type, self.default, self.doc
- )
- }
-}
-
// ==================================================================
// Lint pass
// ==================================================================
@@ -560,7 +548,6 @@ impl<'hir> LateLintPass<'hir> for MetadataCollector {
if is_lint_ref_type(cx, ty);
// disallow check
let lint_name = sym_to_string(item.ident.name).to_ascii_lowercase();
- if !BLACK_LISTED_LINTS.contains(&lint_name.as_str());
// metadata extraction
if let Some((group, level)) = get_lint_group_and_level_or_lint(cx, &lint_name, item);
if let Some(mut raw_docs) = extract_attr_docs_or_lint(cx, item);
@@ -585,7 +572,6 @@ impl<'hir> LateLintPass<'hir> for MetadataCollector {
if is_deprecated_lint(cx, ty);
// disallow check
let lint_name = sym_to_string(item.ident.name).to_ascii_lowercase();
- if !BLACK_LISTED_LINTS.contains(&lint_name.as_str());
// Metadata the little we can get from a deprecated lint
if let Some(raw_docs) = extract_attr_docs_or_lint(cx, item);
then {
@@ -841,7 +827,7 @@ fn collect_renames(lints: &mut Vec<LintMetadata>) {
fn lint_collection_error_item(cx: &LateContext<'_>, item: &Item<'_>, message: &str) {
span_lint(
cx,
- INTERNAL_METADATA_COLLECTOR,
+ METADATA_COLLECTOR,
item.ident.span,
&format!("metadata collection error for `{}`: {message}", item.ident.name),
);
diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs
index a3acb8f17..81be04659 100644
--- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs
@@ -270,7 +270,8 @@ fn read_mir_alloc_def_path<'tcx>(cx: &LateContext<'tcx>, alloc: &'tcx Allocation
if let GlobalAlloc::Memory(alloc) = cx.tcx.global_alloc(alloc) {
let alloc = alloc.inner();
str::from_utf8(alloc.inspect_with_uninit_and_ptr_outside_interpreter(0..alloc.len()))
- .ok().map(ToOwned::to_owned)
+ .ok()
+ .map(ToOwned::to_owned)
} else {
None
}
diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/clippy_lints_internal.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/unsorted_clippy_utils_paths.rs
index da9514dd1..fd51bca9e 100644
--- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/clippy_lints_internal.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints/unsorted_clippy_utils_paths.rs
@@ -5,21 +5,21 @@ use rustc_session::{declare_lint_pass, declare_tool_lint};
declare_clippy_lint! {
/// ### What it does
- /// Checks for various things we like to keep tidy in clippy.
+ /// Checks that [`clippy_utils::paths`] is sorted lexically
///
/// ### Why is this bad?
/// We like to pretend we're an example of tidy code.
///
/// ### Example
/// Wrong ordering of the util::paths constants.
- pub CLIPPY_LINTS_INTERNAL,
+ pub UNSORTED_CLIPPY_UTILS_PATHS,
internal,
"various things that will negatively affect your clippy experience"
}
-declare_lint_pass!(ClippyLintsInternal => [CLIPPY_LINTS_INTERNAL]);
+declare_lint_pass!(UnsortedClippyUtilsPaths => [UNSORTED_CLIPPY_UTILS_PATHS]);
-impl EarlyLintPass for ClippyLintsInternal {
+impl EarlyLintPass for UnsortedClippyUtilsPaths {
fn check_crate(&mut self, cx: &EarlyContext<'_>, krate: &Crate) {
if let Some(utils) = krate.items.iter().find(|item| item.ident.name.as_str() == "utils") {
if let ItemKind::Mod(_, ModKind::Loaded(ref items, ..)) = utils.kind {
@@ -32,7 +32,7 @@ impl EarlyLintPass for ClippyLintsInternal {
if *last_name > *name {
span_lint(
cx,
- CLIPPY_LINTS_INTERNAL,
+ UNSORTED_CLIPPY_UTILS_PATHS,
item.span,
"this constant should be before the previous constant due to lexical \
ordering",
diff --git a/src/tools/clippy/clippy_lints/src/utils/mod.rs b/src/tools/clippy/clippy_lints/src/utils/mod.rs
index 4fef8c071..13e9ead9a 100644
--- a/src/tools/clippy/clippy_lints/src/utils/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/mod.rs
@@ -1,148 +1,5 @@
pub mod author;
-pub mod conf;
pub mod dump_hir;
pub mod format_args_collector;
#[cfg(feature = "internal")]
pub mod internal_lints;
-#[cfg(feature = "internal")]
-use itertools::Itertools;
-
-/// Transforms a given `snake_case_string` to a tasty `kebab-case-string`
-fn to_kebab(config_name: &str) -> String {
- config_name.replace('_', "-")
-}
-
-#[cfg(feature = "internal")]
-const BOOK_CONFIGS_PATH: &str = "https://doc.rust-lang.org/clippy/lint_configuration.html";
-
-// ==================================================================
-// Configuration
-// ==================================================================
-#[derive(Debug, Clone, Default)]
-pub struct ClippyConfiguration {
- pub name: String,
- #[allow(dead_code)]
- config_type: &'static str,
- pub default: String,
- pub lints: Vec<String>,
- pub doc: String,
- #[allow(dead_code)]
- deprecation_reason: Option<&'static str>,
-}
-
-impl ClippyConfiguration {
- pub fn new(
- name: &'static str,
- config_type: &'static str,
- default: String,
- doc_comment: &'static str,
- deprecation_reason: Option<&'static str>,
- ) -> Self {
- let (lints, doc) = parse_config_field_doc(doc_comment)
- .unwrap_or_else(|| (vec![], "[ERROR] MALFORMED DOC COMMENT".to_string()));
-
- Self {
- name: to_kebab(name),
- lints,
- doc,
- config_type,
- default,
- deprecation_reason,
- }
- }
-
- #[cfg(feature = "internal")]
- fn to_markdown_paragraph(&self) -> String {
- format!(
- "## `{}`\n{}\n\n**Default Value:** `{}` (`{}`)\n\n---\n**Affected lints:**\n{}\n\n",
- self.name,
- self.doc
- .lines()
- .map(|line| line.strip_prefix(" ").unwrap_or(line))
- .join("\n"),
- self.default,
- self.config_type,
- self.lints
- .iter()
- .map(|name| name.to_string().split_whitespace().next().unwrap().to_string())
- .map(|name| format!("* [`{name}`](https://rust-lang.github.io/rust-clippy/master/index.html#{name})"))
- .join("\n"),
- )
- }
- #[cfg(feature = "internal")]
- fn to_markdown_link(&self) -> String {
- format!("[`{}`]: {BOOK_CONFIGS_PATH}#{}", self.name, self.name)
- }
-}
-
-#[cfg(feature = "internal")]
-fn collect_configs() -> Vec<ClippyConfiguration> {
- crate::utils::conf::metadata::get_configuration_metadata()
-}
-
-/// This parses the field documentation of the config struct.
-///
-/// ```rust, ignore
-/// parse_config_field_doc(cx, "Lint: LINT_NAME_1, LINT_NAME_2. Papa penguin, papa penguin")
-/// ```
-///
-/// Would yield:
-/// ```rust, ignore
-/// Some(["lint_name_1", "lint_name_2"], "Papa penguin, papa penguin")
-/// ```
-fn parse_config_field_doc(doc_comment: &str) -> Option<(Vec<String>, String)> {
- const DOC_START: &str = " Lint: ";
- if_chain! {
- if doc_comment.starts_with(DOC_START);
- if let Some(split_pos) = doc_comment.find('.');
- then {
- let mut doc_comment = doc_comment.to_string();
- let mut documentation = doc_comment.split_off(split_pos);
-
- // Extract lints
- doc_comment.make_ascii_lowercase();
- let lints: Vec<String> = doc_comment
- .split_off(DOC_START.len())
- .split(", ")
- .map(str::to_string)
- .collect();
-
- // Format documentation correctly
- // split off leading `.` from lint name list and indent for correct formatting
- documentation = documentation.trim_start_matches('.').trim().replace("\n ", "\n ");
-
- Some((lints, documentation))
- } else {
- None
- }
- }
-}
-
-// Shamelessly stolen from find_all (https://github.com/nectariner/find_all)
-pub trait FindAll: Iterator + Sized {
- fn find_all<P>(&mut self, predicate: P) -> Option<Vec<usize>>
- where
- P: FnMut(&Self::Item) -> bool;
-}
-
-impl<I> FindAll for I
-where
- I: Iterator,
-{
- fn find_all<P>(&mut self, mut predicate: P) -> Option<Vec<usize>>
- where
- P: FnMut(&Self::Item) -> bool,
- {
- let mut occurences = Vec::<usize>::default();
- for (index, element) in self.enumerate() {
- if predicate(&element) {
- occurences.push(index);
- }
- }
-
- match occurences.len() {
- 0 => None,
- _ => Some(occurences),
- }
- }
-}
diff --git a/src/tools/clippy/clippy_lints/src/vec.rs b/src/tools/clippy/clippy_lints/src/vec.rs
index fc17e7c6d..a9a3aaad3 100644
--- a/src/tools/clippy/clippy_lints/src/vec.rs
+++ b/src/tools/clippy/clippy_lints/src/vec.rs
@@ -1,8 +1,8 @@
use std::ops::ControlFlow;
+use clippy_config::msrvs::{self, Msrv};
use clippy_utils::consts::{constant, Constant};
use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::ty::is_copy;
use clippy_utils::visitors::for_each_local_use_after_expr;
@@ -14,8 +14,7 @@ use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::layout::LayoutOf;
use rustc_middle::ty::{self, Ty};
use rustc_session::{declare_tool_lint, impl_lint_pass};
-use rustc_span::source_map::Span;
-use rustc_span::sym;
+use rustc_span::{sym, Span};
#[expect(clippy::module_name_repetitions)]
#[derive(Clone)]
@@ -33,14 +32,14 @@ declare_clippy_lint! {
/// This is less efficient.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// fn foo(_x: &[u8]) {}
///
/// foo(&vec![1, 2]);
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # fn foo(_x: &[u8]) {}
/// foo(&[1, 2]);
/// ```
@@ -110,14 +109,15 @@ impl<'tcx> LateLintPass<'tcx> for UselessVec {
} else {
ControlFlow::Break(())
}
- }).is_continue();
+ })
+ .is_continue();
if only_slice_uses {
self.check_vec_macro(
cx,
&vec_args,
expr.span.ctxt().outer_expn_data().call_site,
- SuggestedType::Array
+ SuggestedType::Array,
);
}
}
diff --git a/src/tools/clippy/clippy_lints/src/vec_init_then_push.rs b/src/tools/clippy/clippy_lints/src/vec_init_then_push.rs
index 3fa51216c..c8b9402f1 100644
--- a/src/tools/clippy/clippy_lints/src/vec_init_then_push.rs
+++ b/src/tools/clippy/clippy_lints/src/vec_init_then_push.rs
@@ -30,13 +30,15 @@ declare_clippy_lint! {
/// multiple `push` calls.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let mut v = Vec::new();
/// v.push(0);
+ /// v.push(1);
+ /// v.push(2);
/// ```
/// Use instead:
- /// ```rust
- /// let v = vec![0];
+ /// ```no_run
+ /// let v = vec![0, 1, 2];
/// ```
#[clippy::version = "1.51.0"]
pub VEC_INIT_THEN_PUSH,
@@ -209,7 +211,7 @@ impl<'tcx> LateLintPass<'tcx> for VecInitThenPush {
found: searcher.found + 1,
err_span: searcher.err_span.to(stmt.span),
last_push_expr: expr.hir_id,
- .. searcher
+ ..searcher
});
} else {
searcher.display_err(cx);
diff --git a/src/tools/clippy/clippy_lints/src/visibility.rs b/src/tools/clippy/clippy_lints/src/visibility.rs
index 496376520..8abcc964b 100644
--- a/src/tools/clippy/clippy_lints/src/visibility.rs
+++ b/src/tools/clippy/clippy_lints/src/visibility.rs
@@ -82,7 +82,9 @@ impl EarlyLintPass for Visibility {
if !in_external_macro(cx.sess(), item.span)
&& let VisibilityKind::Restricted { path, shorthand, .. } = &item.vis.kind
{
- if **path == kw::SelfLower && let Some(false) = is_from_proc_macro(cx, item.vis.span) {
+ if **path == kw::SelfLower
+ && let Some(false) = is_from_proc_macro(cx, item.vis.span)
+ {
span_lint_and_sugg(
cx,
NEEDLESS_PUB_SELF,
diff --git a/src/tools/clippy/clippy_lints/src/wildcard_imports.rs b/src/tools/clippy/clippy_lints/src/wildcard_imports.rs
index d09d02a7d..d88ede763 100644
--- a/src/tools/clippy/clippy_lints/src/wildcard_imports.rs
+++ b/src/tools/clippy/clippy_lints/src/wildcard_imports.rs
@@ -24,7 +24,7 @@ declare_clippy_lint! {
/// still around.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// use std::cmp::Ordering::*;
///
/// # fn foo(_: std::cmp::Ordering) {}
@@ -32,7 +32,7 @@ declare_clippy_lint! {
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// use std::cmp::Ordering;
///
/// # fn foo(_: Ordering) {}
@@ -132,6 +132,7 @@ impl LateLintPass<'_> for WildcardImports {
if self.warn_on_all || !self.check_exceptions(item, use_path.segments);
let used_imports = cx.tcx.names_imported_by_glob_use(item.owner_id.def_id);
if !used_imports.is_empty(); // Already handled by `unused_imports`
+ if !used_imports.contains(&kw::Underscore);
then {
let mut applicability = Applicability::MachineApplicable;
let import_source_snippet = snippet_with_applicability(cx, use_path.span, "..", &mut applicability);
diff --git a/src/tools/clippy/clippy_lints/src/write.rs b/src/tools/clippy/clippy_lints/src/write.rs
index da083fb14..b6f942a90 100644
--- a/src/tools/clippy/clippy_lints/src/write.rs
+++ b/src/tools/clippy/clippy_lints/src/write.rs
@@ -3,12 +3,15 @@ use clippy_utils::macros::{find_format_args, format_arg_removal_span, root_macro
use clippy_utils::source::{expand_past_previous_comma, snippet_opt};
use clippy_utils::{is_in_cfg_test, is_in_test_function};
use rustc_ast::token::LitKind;
-use rustc_ast::{FormatArgPosition, FormatArgs, FormatArgsPiece, FormatOptions, FormatPlaceholder, FormatTrait};
+use rustc_ast::{
+ FormatArgPosition, FormatArgPositionKind, FormatArgs, FormatArgsPiece, FormatOptions, FormatPlaceholder,
+ FormatTrait,
+};
use rustc_errors::Applicability;
use rustc_hir::{Expr, Impl, Item, ItemKind};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_session::{declare_tool_lint, impl_lint_pass};
-use rustc_span::{sym, BytePos};
+use rustc_span::{sym, BytePos, Span};
declare_clippy_lint! {
/// ### What it does
@@ -19,12 +22,12 @@ declare_clippy_lint! {
/// You should use `println!()`, which is simpler.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// println!("");
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// println!();
/// ```
#[clippy::version = "pre 1.29.0"]
@@ -43,12 +46,12 @@ declare_clippy_lint! {
/// newline.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # let name = "World";
/// print!("Hello {}!\n", name);
/// ```
/// use println!() instead
- /// ```rust
+ /// ```no_run
/// # let name = "World";
/// println!("Hello {}!", name);
/// ```
@@ -71,7 +74,7 @@ declare_clippy_lint! {
/// Only catches `print!` and `println!` calls.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// println!("Hello world!");
/// ```
#[clippy::version = "pre 1.29.0"]
@@ -93,7 +96,7 @@ declare_clippy_lint! {
/// Only catches `eprint!` and `eprintln!` calls.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// eprintln!("Hello world!");
/// ```
#[clippy::version = "1.50.0"]
@@ -112,7 +115,7 @@ declare_clippy_lint! {
/// debugging Rust code. It should not be used in user-facing output.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # let foo = "bar";
/// println!("{:?}", foo);
/// ```
@@ -132,11 +135,11 @@ declare_clippy_lint! {
/// (i.e., just put the literal in the format string)
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// println!("{}", "foo");
/// ```
/// use the literal without formatting:
- /// ```rust
+ /// ```no_run
/// println!("foo");
/// ```
#[clippy::version = "pre 1.29.0"]
@@ -154,14 +157,14 @@ declare_clippy_lint! {
/// You should use `writeln!(buf)`, which is simpler.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # use std::fmt::Write;
/// # let mut buf = String::new();
/// writeln!(buf, "");
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # use std::fmt::Write;
/// # let mut buf = String::new();
/// writeln!(buf);
@@ -183,7 +186,7 @@ declare_clippy_lint! {
/// newline.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # use std::fmt::Write;
/// # let mut buf = String::new();
/// # let name = "World";
@@ -191,7 +194,7 @@ declare_clippy_lint! {
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # use std::fmt::Write;
/// # let mut buf = String::new();
/// # let name = "World";
@@ -213,14 +216,14 @@ declare_clippy_lint! {
/// (i.e., just put the literal in the format string)
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # use std::fmt::Write;
/// # let mut buf = String::new();
/// writeln!(buf, "{}", "foo");
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # use std::fmt::Write;
/// # let mut buf = String::new();
/// writeln!(buf, "foo");
@@ -339,7 +342,10 @@ impl<'tcx> LateLintPass<'tcx> for Write {
}
fn is_debug_impl(cx: &LateContext<'_>, item: &Item<'_>) -> bool {
- if let ItemKind::Impl(Impl { of_trait: Some(trait_ref), .. }) = &item.kind
+ if let ItemKind::Impl(Impl {
+ of_trait: Some(trait_ref),
+ ..
+ }) = &item.kind
&& let Some(trait_id) = trait_ref.trait_def_id()
{
cx.tcx.is_diagnostic_item(sym::Debug, trait_id)
@@ -450,6 +456,12 @@ fn check_empty_string(cx: &LateContext<'_>, format_args: &FormatArgs, macro_call
fn check_literal(cx: &LateContext<'_>, format_args: &FormatArgs, name: &str) {
let arg_index = |argument: &FormatArgPosition| argument.index.unwrap_or_else(|pos| pos);
+ let lint_name = if name.starts_with("write") {
+ WRITE_LITERAL
+ } else {
+ PRINT_LITERAL
+ };
+
let mut counts = vec![0u32; format_args.arguments.all_args().len()];
for piece in &format_args.template {
if let FormatArgsPiece::Placeholder(placeholder) = piece {
@@ -457,6 +469,12 @@ fn check_literal(cx: &LateContext<'_>, format_args: &FormatArgs, name: &str) {
}
}
+ let mut suggestion: Vec<(Span, String)> = vec![];
+ // holds index of replaced positional arguments; used to decrement the index of the remaining
+ // positional arguments.
+ let mut replaced_position: Vec<usize> = vec![];
+ let mut sug_span: Option<Span> = None;
+
for piece in &format_args.template {
if let FormatArgsPiece::Placeholder(FormatPlaceholder {
argument,
@@ -471,9 +489,9 @@ fn check_literal(cx: &LateContext<'_>, format_args: &FormatArgs, name: &str) {
&& let rustc_ast::ExprKind::Lit(lit) = &arg.expr.kind
&& !arg.expr.span.from_expansion()
&& let Some(value_string) = snippet_opt(cx, arg.expr.span)
- {
+ {
let (replacement, replace_raw) = match lit.kind {
- LitKind::Str | LitKind::StrRaw(_) => match extract_str_literal(&value_string) {
+ LitKind::Str | LitKind::StrRaw(_) => match extract_str_literal(&value_string) {
Some(extracted) => extracted,
None => return,
},
@@ -493,13 +511,9 @@ fn check_literal(cx: &LateContext<'_>, format_args: &FormatArgs, name: &str) {
_ => continue,
};
- let lint = if name.starts_with("write") {
- WRITE_LITERAL
- } else {
- PRINT_LITERAL
+ let Some(format_string_snippet) = snippet_opt(cx, format_args.span) else {
+ continue;
};
-
- let Some(format_string_snippet) = snippet_opt(cx, format_args.span) else { continue };
let format_string_is_raw = format_string_snippet.starts_with('r');
let replacement = match (format_string_is_raw, replace_raw) {
@@ -519,29 +533,60 @@ fn check_literal(cx: &LateContext<'_>, format_args: &FormatArgs, name: &str) {
},
};
- span_lint_and_then(
- cx,
- lint,
- arg.expr.span,
- "literal with an empty format string",
- |diag| {
- if let Some(replacement) = replacement
- // `format!("{}", "a")`, `format!("{named}", named = "b")
- // ~~~~~ ~~~~~~~~~~~~~
- && let Some(removal_span) = format_arg_removal_span(format_args, index)
- {
- let replacement = replacement.replace('{', "{{").replace('}', "}}");
- diag.multipart_suggestion(
- "try",
- vec![(*placeholder_span, replacement), (removal_span, String::new())],
- Applicability::MachineApplicable,
- );
- }
- },
- );
+ sug_span = Some(sug_span.unwrap_or(arg.expr.span).to(arg.expr.span));
+ if let Some((_, index)) = positional_arg_piece_span(piece) {
+ replaced_position.push(index);
+ }
+
+ if let Some(replacement) = replacement
+ // `format!("{}", "a")`, `format!("{named}", named = "b")
+ // ~~~~~ ~~~~~~~~~~~~~
+ && let Some(removal_span) = format_arg_removal_span(format_args, index)
+ {
+ let replacement = escape_braces(&replacement, !format_string_is_raw && !replace_raw);
+ suggestion.push((*placeholder_span, replacement));
+ suggestion.push((removal_span, String::new()));
+ }
+ }
+ }
+
+ // Decrement the index of the remaining by the number of replaced positional arguments
+ if !suggestion.is_empty() {
+ for piece in &format_args.template {
+ if let Some((span, index)) = positional_arg_piece_span(piece)
+ && suggestion.iter().all(|(s, _)| *s != span)
+ {
+ let decrement = replaced_position.iter().filter(|i| **i < index).count();
+ suggestion.push((span, format!("{{{}}}", index.saturating_sub(decrement))));
+ }
}
}
+
+ if let Some(span) = sug_span {
+ span_lint_and_then(cx, lint_name, span, "literal with an empty format string", |diag| {
+ if !suggestion.is_empty() {
+ diag.multipart_suggestion("try", suggestion, Applicability::MachineApplicable);
+ }
+ });
+ }
+}
+
+/// Extract Span and its index from the given `piece`, iff it's positional argument.
+fn positional_arg_piece_span(piece: &FormatArgsPiece) -> Option<(Span, usize)> {
+ match piece {
+ FormatArgsPiece::Placeholder(FormatPlaceholder {
+ argument:
+ FormatArgPosition {
+ index: Ok(index),
+ kind: FormatArgPositionKind::Number,
+ ..
+ },
+ span: Some(span),
+ ..
+ }) => Some((*span, *index)),
+ _ => None,
+ }
}
/// Removes the raw marker, `#`s and quotes from a str, and returns if the literal is raw
@@ -593,3 +638,47 @@ fn conservative_unescape(literal: &str) -> Result<String, UnescapeErr> {
if err { Err(UnescapeErr::Lint) } else { Ok(unescaped) }
}
+
+/// Replaces `{` with `{{` and `}` with `}}`. If `preserve_unicode_escapes` is `true` the braces in
+/// `\u{xxxx}` are left unmodified
+#[expect(clippy::match_same_arms)]
+fn escape_braces(literal: &str, preserve_unicode_escapes: bool) -> String {
+ #[derive(Clone, Copy)]
+ enum State {
+ Normal,
+ Backslash,
+ UnicodeEscape,
+ }
+
+ let mut escaped = String::with_capacity(literal.len());
+ let mut state = State::Normal;
+
+ for ch in literal.chars() {
+ state = match (ch, state) {
+ // Escape braces outside of unicode escapes by doubling them up
+ ('{' | '}', State::Normal) => {
+ escaped.push(ch);
+ State::Normal
+ },
+ // If `preserve_unicode_escapes` isn't enabled stay in `State::Normal`, otherwise:
+ //
+ // \u{aaaa} \\ \x01
+ // ^ ^ ^
+ ('\\', State::Normal) if preserve_unicode_escapes => State::Backslash,
+ // \u{aaaa}
+ // ^
+ ('u', State::Backslash) => State::UnicodeEscape,
+ // \xAA \\
+ // ^ ^
+ (_, State::Backslash) => State::Normal,
+ // \u{aaaa}
+ // ^
+ ('}', State::UnicodeEscape) => State::Normal,
+ _ => state,
+ };
+
+ escaped.push(ch);
+ }
+
+ escaped
+}
diff --git a/src/tools/clippy/clippy_lints/src/zero_div_zero.rs b/src/tools/clippy/clippy_lints/src/zero_div_zero.rs
index 9b3de35db..f2f0699ef 100644
--- a/src/tools/clippy/clippy_lints/src/zero_div_zero.rs
+++ b/src/tools/clippy/clippy_lints/src/zero_div_zero.rs
@@ -13,12 +13,12 @@ declare_clippy_lint! {
/// It's less readable than `f32::NAN` or `f64::NAN`.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// let nan = 0.0f32 / 0.0;
/// ```
///
/// Use instead:
- /// ```rust
+ /// ```no_run
/// let nan = f32::NAN;
/// ```
#[clippy::version = "pre 1.29.0"]
diff --git a/src/tools/clippy/clippy_lints/src/zero_sized_map_values.rs b/src/tools/clippy/clippy_lints/src/zero_sized_map_values.rs
index 002304f88..fee100fe1 100644
--- a/src/tools/clippy/clippy_lints/src/zero_sized_map_values.rs
+++ b/src/tools/clippy/clippy_lints/src/zero_sized_map_values.rs
@@ -23,14 +23,14 @@ declare_clippy_lint! {
/// * This lints the signature of public items
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # use std::collections::HashMap;
/// fn unique_words(text: &str) -> HashMap<&str, ()> {
/// todo!();
/// }
/// ```
/// Use instead:
- /// ```rust
+ /// ```no_run
/// # use std::collections::HashSet;
/// fn unique_words(text: &str) -> HashSet<&str> {
/// todo!();
diff --git a/src/tools/clippy/clippy_utils/Cargo.toml b/src/tools/clippy/clippy_utils/Cargo.toml
index 1596bb773..c9b01a68f 100644
--- a/src/tools/clippy/clippy_utils/Cargo.toml
+++ b/src/tools/clippy/clippy_utils/Cargo.toml
@@ -1,18 +1,18 @@
[package]
name = "clippy_utils"
-version = "0.1.74"
+version = "0.1.75"
edition = "2021"
publish = false
[dependencies]
+clippy_config = { path = "../clippy_config" }
arrayvec = { version = "0.7", default-features = false }
if_chain = "1.0"
itertools = "0.10.1"
rustc-semver = "1.1"
[features]
-deny-warnings = []
-internal = []
+deny-warnings = ["clippy_config/deny-warnings"]
[package.metadata.rust-analyzer]
# This crate uses #[feature(rustc_private)]
diff --git a/src/tools/clippy/clippy_utils/src/ast_utils.rs b/src/tools/clippy/clippy_utils/src/ast_utils.rs
index a78ff0202..a2c61e07b 100644
--- a/src/tools/clippy/clippy_utils/src/ast_utils.rs
+++ b/src/tools/clippy/clippy_utils/src/ast_utils.rs
@@ -211,7 +211,7 @@ pub fn eq_expr(l: &Expr, r: &Expr) -> bool {
&& eq_fn_decl(lf, rf)
&& eq_expr(le, re)
},
- (Async(lc, lb), Async(rc, rb)) => lc == rc && eq_block(lb, rb),
+ (Gen(lc, lb, lk), Gen(rc, rb, rk)) => lc == rc && eq_block(lb, rb) && lk == rk,
(Range(lf, lt, ll), Range(rf, rt, rl)) => ll == rl && eq_expr_opt(lf, rf) && eq_expr_opt(lt, rt),
(AddrOf(lbk, lm, le), AddrOf(rbk, rm, re)) => lbk == rbk && lm == rm && eq_expr(le, re),
(Path(lq, lp), Path(rq, rp)) => both(lq, rq, eq_qself) && eq_path(lp, rp),
diff --git a/src/tools/clippy/clippy_utils/src/check_proc_macro.rs b/src/tools/clippy/clippy_utils/src/check_proc_macro.rs
index 6be8b8bb9..2f619a306 100644
--- a/src/tools/clippy/clippy_utils/src/check_proc_macro.rs
+++ b/src/tools/clippy/clippy_utils/src/check_proc_macro.rs
@@ -12,14 +12,14 @@
//! code was written, and check if the span contains that text. Note this will only work correctly
//! if the span is not from a `macro_rules` based macro.
-use rustc_ast::ast::{AttrKind, Attribute, IntTy, LitIntType, LitKind, StrStyle, UintTy};
+use rustc_ast::ast::{AttrKind, Attribute, IntTy, LitIntType, LitKind, StrStyle, TraitObjectSyntax, UintTy};
use rustc_ast::token::CommentKind;
use rustc_ast::AttrStyle;
use rustc_hir::intravisit::FnKind;
use rustc_hir::{
- Block, BlockCheckMode, Body, Closure, Destination, Expr, ExprKind, FieldDef, FnHeader, HirId, Impl, ImplItem,
- ImplItemKind, IsAuto, Item, ItemKind, LoopSource, MatchSource, MutTy, Node, QPath, TraitItem, TraitItemKind, Ty,
- TyKind, UnOp, UnsafeSource, Unsafety, Variant, VariantData, YieldSource,
+ Block, BlockCheckMode, Body, Closure, Destination, Expr, ExprKind, FieldDef, FnHeader, FnRetTy, HirId, Impl,
+ ImplItem, ImplItemKind, IsAuto, Item, ItemKind, LoopSource, MatchSource, MutTy, Node, QPath, TraitItem,
+ TraitItemKind, Ty, TyKind, UnOp, UnsafeSource, Unsafety, Variant, VariantData, YieldSource,
};
use rustc_lint::{LateContext, LintContext};
use rustc_middle::ty::TyCtxt;
@@ -33,8 +33,6 @@ use rustc_target::spec::abi::Abi;
pub enum Pat {
/// A single string.
Str(&'static str),
- /// A single string.
- OwnedStr(String),
/// Any of the given strings.
MultiStr(&'static [&'static str]),
/// Any of the given strings.
@@ -59,14 +57,12 @@ fn span_matches_pat(sess: &Session, span: Span, start_pat: Pat, end_pat: Pat) ->
let end_str = s.trim_end_matches(|c: char| c.is_whitespace() || c == ')' || c == ',');
(match start_pat {
Pat::Str(text) => start_str.starts_with(text),
- Pat::OwnedStr(text) => start_str.starts_with(&text),
Pat::MultiStr(texts) => texts.iter().any(|s| start_str.starts_with(s)),
Pat::OwnedMultiStr(texts) => texts.iter().any(|s| start_str.starts_with(s)),
Pat::Sym(sym) => start_str.starts_with(sym.as_str()),
Pat::Num => start_str.as_bytes().first().map_or(false, u8::is_ascii_digit),
} && match end_pat {
Pat::Str(text) => end_str.ends_with(text),
- Pat::OwnedStr(text) => end_str.starts_with(&text),
Pat::MultiStr(texts) => texts.iter().any(|s| start_str.ends_with(s)),
Pat::OwnedMultiStr(texts) => texts.iter().any(|s| start_str.starts_with(s)),
Pat::Sym(sym) => end_str.ends_with(sym.as_str()),
@@ -125,6 +121,8 @@ fn qpath_search_pat(path: &QPath<'_>) -> (Pat, Pat) {
fn expr_search_pat(tcx: TyCtxt<'_>, e: &Expr<'_>) -> (Pat, Pat) {
match e.kind {
ExprKind::ConstBlock(_) => (Pat::Str("const"), Pat::Str("}")),
+ // Parenthesis are trimmed from the text before the search patterns are matched.
+ // See: `span_matches_pat`
ExprKind::Tup([]) => (Pat::Str(")"), Pat::Str("(")),
ExprKind::Unary(UnOp::Deref, e) => (Pat::Str("*"), expr_search_pat(tcx, e).1),
ExprKind::Unary(UnOp::Not, e) => (Pat::Str("!"), expr_search_pat(tcx, e).1),
@@ -286,21 +284,17 @@ fn fn_kind_pat(tcx: TyCtxt<'_>, kind: &FnKind<'_>, body: &Body<'_>, hir_id: HirI
fn attr_search_pat(attr: &Attribute) -> (Pat, Pat) {
match attr.kind {
AttrKind::Normal(..) => {
- let mut pat = if matches!(attr.style, AttrStyle::Outer) {
- (Pat::Str("#["), Pat::Str("]"))
- } else {
- (Pat::Str("#!["), Pat::Str("]"))
- };
-
- if let Some(ident) = attr.ident() && let Pat::Str(old_pat) = pat.0 {
+ if let Some(ident) = attr.ident() {
// TODO: I feel like it's likely we can use `Cow` instead but this will require quite a bit of
// refactoring
// NOTE: This will likely have false positives, like `allow = 1`
- pat.0 = Pat::OwnedMultiStr(vec![ident.to_string(), old_pat.to_owned()]);
- pat.1 = Pat::Str("");
+ (
+ Pat::OwnedMultiStr(vec![ident.to_string(), "#".to_owned()]),
+ Pat::Str(""),
+ )
+ } else {
+ (Pat::Str("#"), Pat::Str("]"))
}
-
- pat
},
AttrKind::DocComment(_kind @ CommentKind::Line, ..) => {
if matches!(attr.style, AttrStyle::Outer) {
@@ -322,32 +316,42 @@ fn attr_search_pat(attr: &Attribute) -> (Pat, Pat) {
fn ty_search_pat(ty: &Ty<'_>) -> (Pat, Pat) {
match ty.kind {
TyKind::Slice(..) | TyKind::Array(..) => (Pat::Str("["), Pat::Str("]")),
- TyKind::Ptr(MutTy { mutbl, ty }) => (
- if mutbl.is_mut() {
- Pat::Str("*const")
- } else {
- Pat::Str("*mut")
- },
- ty_search_pat(ty).1,
- ),
+ TyKind::Ptr(MutTy { ty, .. }) => (Pat::Str("*"), ty_search_pat(ty).1),
TyKind::Ref(_, MutTy { ty, .. }) => (Pat::Str("&"), ty_search_pat(ty).1),
TyKind::BareFn(bare_fn) => (
- Pat::OwnedStr(format!("{}{} fn", bare_fn.unsafety.prefix_str(), bare_fn.abi.name())),
- ty_search_pat(ty).1,
+ if bare_fn.unsafety == Unsafety::Unsafe {
+ Pat::Str("unsafe")
+ } else if bare_fn.abi != Abi::Rust {
+ Pat::Str("extern")
+ } else {
+ Pat::MultiStr(&["fn", "extern"])
+ },
+ match bare_fn.decl.output {
+ FnRetTy::DefaultReturn(_) => {
+ if let [.., ty] = bare_fn.decl.inputs {
+ ty_search_pat(ty).1
+ } else {
+ Pat::Str("(")
+ }
+ },
+ FnRetTy::Return(ty) => ty_search_pat(ty).1,
+ },
),
- TyKind::Never => (Pat::Str("!"), Pat::Str("")),
- TyKind::Tup(..) => (Pat::Str("("), Pat::Str(")")),
+ TyKind::Never => (Pat::Str("!"), Pat::Str("!")),
+ // Parenthesis are trimmed from the text before the search patterns are matched.
+ // See: `span_matches_pat`
+ TyKind::Tup([]) => (Pat::Str(")"), Pat::Str("(")),
+ TyKind::Tup([ty]) => ty_search_pat(ty),
+ TyKind::Tup([head, .., tail]) => (ty_search_pat(head).0, ty_search_pat(tail).1),
TyKind::OpaqueDef(..) => (Pat::Str("impl"), Pat::Str("")),
TyKind::Path(qpath) => qpath_search_pat(&qpath),
- // NOTE: This is missing `TraitObject`. It will always return true then.
+ TyKind::Infer => (Pat::Str("_"), Pat::Str("_")),
+ TyKind::TraitObject(_, _, TraitObjectSyntax::Dyn) => (Pat::Str("dyn"), Pat::Str("")),
+ // NOTE: `TraitObject` is incomplete. It will always return true then.
_ => (Pat::Str(""), Pat::Str("")),
}
}
-fn ident_search_pat(ident: Ident) -> (Pat, Pat) {
- (Pat::OwnedStr(ident.name.as_str().to_owned()), Pat::Str(""))
-}
-
pub trait WithSearchPat<'cx> {
type Context: LintContext;
fn search_pat(&self, cx: &Self::Context) -> (Pat, Pat);
@@ -406,7 +410,7 @@ impl<'cx> WithSearchPat<'cx> for Ident {
type Context = LateContext<'cx>;
fn search_pat(&self, _cx: &Self::Context) -> (Pat, Pat) {
- ident_search_pat(*self)
+ (Pat::Sym(self.name), Pat::Sym(self.name))
}
fn span(&self) -> Span {
diff --git a/src/tools/clippy/clippy_utils/src/consts.rs b/src/tools/clippy/clippy_utils/src/consts.rs
index d596eed4b..b581a60de 100644
--- a/src/tools/clippy/clippy_utils/src/consts.rs
+++ b/src/tools/clippy/clippy_utils/src/consts.rs
@@ -9,11 +9,12 @@ use rustc_hir::def::{DefKind, Res};
use rustc_hir::{BinOp, BinOpKind, Block, ConstBlock, Expr, ExprKind, HirId, Item, ItemKind, Node, QPath, UnOp};
use rustc_lexer::tokenize;
use rustc_lint::LateContext;
-use rustc_middle::mir::interpret::Scalar;
+use rustc_middle::mir::interpret::{alloc_range, Scalar};
use rustc_middle::ty::{self, EarlyBinder, FloatTy, GenericArgsRef, List, ScalarInt, Ty, TyCtxt};
use rustc_middle::{bug, mir, span_bug};
use rustc_span::symbol::{Ident, Symbol};
use rustc_span::SyntaxContext;
+use rustc_target::abi::Size;
use std::cmp::Ordering::{self, Equal};
use std::hash::{Hash, Hasher};
use std::iter;
@@ -403,9 +404,8 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
&& adt_def.is_struct()
&& let Some(desired_field) = field_of_struct(*adt_def, self.lcx, *constant, field)
{
- miri_to_const(self.lcx, desired_field)
- }
- else {
+ mir_to_const(self.lcx, desired_field)
+ } else {
result
}
},
@@ -461,11 +461,15 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
// Check if this constant is based on `cfg!(..)`,
// which is NOT constant for our purposes.
if let Some(node) = self.lcx.tcx.hir().get_if_local(def_id)
- && let Node::Item(Item { kind: ItemKind::Const(.., body_id), .. }) = node
- && let Node::Expr(Expr { kind: ExprKind::Lit(_), span, .. }) = self.lcx
- .tcx
- .hir()
- .get(body_id.hir_id)
+ && let Node::Item(Item {
+ kind: ItemKind::Const(.., body_id),
+ ..
+ }) = node
+ && let Node::Expr(Expr {
+ kind: ExprKind::Lit(_),
+ span,
+ ..
+ }) = self.lcx.tcx.hir().get(body_id.hir_id)
&& is_direct_expn_of(*span, "cfg").is_some()
{
return None;
@@ -483,7 +487,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
.const_eval_resolve(self.param_env, mir::UnevaluatedConst::new(def_id, args), None)
.ok()
.map(|val| rustc_middle::mir::Const::from_value(val, ty))?;
- let result = miri_to_const(self.lcx, result)?;
+ let result = mir_to_const(self.lcx, result)?;
self.source = ConstantSource::Constant;
Some(result)
},
@@ -503,7 +507,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
},
(Some(Constant::Vec(vec)), _) => {
if !vec.is_empty() && vec.iter().all(|x| *x == vec[0]) {
- match vec.get(0) {
+ match vec.first() {
Some(Constant::F32(x)) => Some(Constant::F32(*x)),
Some(Constant::F64(x)) => Some(Constant::F64(*x)),
_ => None,
@@ -530,7 +534,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
&& let Some(src) = get_source_text(self.lcx, span.lo..expr_lo)
&& let Some(src) = src.as_str()
{
- use rustc_lexer::TokenKind::{Whitespace, LineComment, BlockComment, Semi, OpenBrace};
+ use rustc_lexer::TokenKind::{BlockComment, LineComment, OpenBrace, Semi, Whitespace};
if !tokenize(src)
.map(|t| t.kind)
.filter(|t| !matches!(t, Whitespace | LineComment { .. } | BlockComment { .. } | Semi))
@@ -655,10 +659,14 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
}
}
-pub fn miri_to_const<'tcx>(lcx: &LateContext<'tcx>, result: mir::Const<'tcx>) -> Option<Constant<'tcx>> {
+pub fn mir_to_const<'tcx>(lcx: &LateContext<'tcx>, result: mir::Const<'tcx>) -> Option<Constant<'tcx>> {
use rustc_middle::mir::ConstValue;
- match result {
- mir::Const::Val(ConstValue::Scalar(Scalar::Int(int)), _) => match result.ty().kind() {
+ let mir::Const::Val(val, _) = result else {
+ // We only work on evaluated consts.
+ return None;
+ };
+ match (val, result.ty().kind()) {
+ (ConstValue::Scalar(Scalar::Int(int)), _) => match result.ty().kind() {
ty::Adt(adt_def, _) if adt_def.is_struct() => Some(Constant::Adt(result)),
ty::Bool => Some(Constant::Bool(int == ScalarInt::TRUE)),
ty::Uint(_) | ty::Int(_) => Some(Constant::Int(int.assert_bits(int.size()))),
@@ -671,42 +679,28 @@ pub fn miri_to_const<'tcx>(lcx: &LateContext<'tcx>, result: mir::Const<'tcx>) ->
ty::RawPtr(_) => Some(Constant::RawPtr(int.assert_bits(int.size()))),
_ => None,
},
- mir::Const::Val(cv, _) if matches!(result.ty().kind(), ty::Ref(_, inner_ty, _) if matches!(inner_ty.kind(), ty::Str)) =>
- {
- let data = cv.try_get_slice_bytes_for_diagnostics(lcx.tcx)?;
+ (_, ty::Ref(_, inner_ty, _)) if matches!(inner_ty.kind(), ty::Str) => {
+ let data = val.try_get_slice_bytes_for_diagnostics(lcx.tcx)?;
String::from_utf8(data.to_owned()).ok().map(Constant::Str)
},
- mir::Const::Val(ConstValue::Indirect { alloc_id, offset: _ }, _) => {
- let alloc = lcx.tcx.global_alloc(alloc_id).unwrap_memory();
- match result.ty().kind() {
- ty::Adt(adt_def, _) if adt_def.is_struct() => Some(Constant::Adt(result)),
- ty::Array(sub_type, len) => match sub_type.kind() {
- ty::Float(FloatTy::F32) => match len.try_to_target_usize(lcx.tcx) {
- Some(len) => alloc
- .inner()
- .inspect_with_uninit_and_ptr_outside_interpreter(0..(4 * usize::try_from(len).unwrap()))
- .to_owned()
- .array_chunks::<4>()
- .map(|&chunk| Some(Constant::F32(f32::from_le_bytes(chunk))))
- .collect::<Option<Vec<Constant<'tcx>>>>()
- .map(Constant::Vec),
- _ => None,
- },
- ty::Float(FloatTy::F64) => match len.try_to_target_usize(lcx.tcx) {
- Some(len) => alloc
- .inner()
- .inspect_with_uninit_and_ptr_outside_interpreter(0..(8 * usize::try_from(len).unwrap()))
- .to_owned()
- .array_chunks::<8>()
- .map(|&chunk| Some(Constant::F64(f64::from_le_bytes(chunk))))
- .collect::<Option<Vec<Constant<'tcx>>>>()
- .map(Constant::Vec),
- _ => None,
- },
- _ => None,
- },
- _ => None,
+ (_, ty::Adt(adt_def, _)) if adt_def.is_struct() => Some(Constant::Adt(result)),
+ (ConstValue::Indirect { alloc_id, offset }, ty::Array(sub_type, len)) => {
+ let alloc = lcx.tcx.global_alloc(alloc_id).unwrap_memory().inner();
+ let len = len.try_to_target_usize(lcx.tcx)?;
+ let ty::Float(flt) = sub_type.kind() else {
+ return None;
+ };
+ let size = Size::from_bits(flt.bit_width());
+ let mut res = Vec::new();
+ for idx in 0..len {
+ let range = alloc_range(offset + size * idx, size);
+ let val = alloc.read_scalar(&lcx.tcx, range, /* read_provenance */ false).ok()?;
+ res.push(match flt {
+ FloatTy::F32 => Constant::F32(f32::from_bits(val.to_u32().ok()?)),
+ FloatTy::F64 => Constant::F64(f64::from_bits(val.to_u64().ok()?)),
+ });
}
+ Some(Constant::Vec(res))
},
_ => None,
}
@@ -719,15 +713,14 @@ fn field_of_struct<'tcx>(
field: &Ident,
) -> Option<mir::Const<'tcx>> {
if let mir::Const::Val(result, ty) = result
- && let Some(dc) = lcx.tcx.try_destructure_mir_constant_for_diagnostics(result, ty)
+ && let Some(dc) = lcx.tcx.try_destructure_mir_constant_for_user_output(result, ty)
&& let Some(dc_variant) = dc.variant
&& let Some(variant) = adt_def.variants().get(dc_variant)
&& let Some(field_idx) = variant.fields.iter().position(|el| el.name == field.name)
&& let Some(&(val, ty)) = dc.fields.get(field_idx)
{
Some(mir::Const::Val(val, ty))
- }
- else {
+ } else {
None
}
}
diff --git a/src/tools/clippy/clippy_utils/src/diagnostics.rs b/src/tools/clippy/clippy_utils/src/diagnostics.rs
index edd87546a..45c7e3a6e 100644
--- a/src/tools/clippy/clippy_utils/src/diagnostics.rs
+++ b/src/tools/clippy/clippy_utils/src/diagnostics.rs
@@ -11,7 +11,7 @@
use rustc_errors::{Applicability, Diagnostic, MultiSpan};
use rustc_hir::HirId;
use rustc_lint::{LateContext, Lint, LintContext};
-use rustc_span::source_map::Span;
+use rustc_span::Span;
use std::env;
fn docs_link(diag: &mut Diagnostic, lint: &'static Lint) {
@@ -193,7 +193,7 @@ pub fn span_lint_hir_and_then(
/// |
/// = note: `-D fold-any` implied by `-D warnings`
/// ```
-#[cfg_attr(feature = "internal", allow(clippy::collapsible_span_lint_calls))]
+#[expect(clippy::collapsible_span_lint_calls)]
pub fn span_lint_and_sugg<T: LintContext>(
cx: &T,
lint: &'static Lint,
diff --git a/src/tools/clippy/clippy_utils/src/higher.rs b/src/tools/clippy/clippy_utils/src/higher.rs
index 802adbd4d..edea4b366 100644
--- a/src/tools/clippy/clippy_utils/src/higher.rs
+++ b/src/tools/clippy/clippy_utils/src/higher.rs
@@ -449,7 +449,7 @@ pub fn get_vec_init_kind<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -
} else if name.ident.name == symbol::kw::Default {
return Some(VecInitKind::Default);
} else if name.ident.name.as_str() == "with_capacity" {
- let arg = args.get(0)?;
+ let arg = args.first()?;
return match constant_simple(cx, cx.typeck_results(), arg) {
Some(Constant::Int(num)) => Some(VecInitKind::WithConstCapacity(num)),
_ => Some(VecInitKind::WithExprCapacity(arg.hir_id)),
@@ -457,7 +457,7 @@ pub fn get_vec_init_kind<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -
};
},
ExprKind::Path(QPath::Resolved(_, path))
- if match_def_path(cx, path.res.opt_def_id()?, &paths::DEFAULT_TRAIT_METHOD)
+ if cx.tcx.is_diagnostic_item(sym::default_fn, path.res.opt_def_id()?)
&& is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::Vec) =>
{
return Some(VecInitKind::Default);
diff --git a/src/tools/clippy/clippy_utils/src/hir_utils.rs b/src/tools/clippy/clippy_utils/src/hir_utils.rs
index 52214e733..2a8b2ebd5 100644
--- a/src/tools/clippy/clippy_utils/src/hir_utils.rs
+++ b/src/tools/clippy/clippy_utils/src/hir_utils.rs
@@ -1142,12 +1142,8 @@ fn eq_span_tokens(
let pred = |t: &(_, _)| pred(t.0);
let map = |(_, x)| x;
- let ltok = tokenize_with_text(lsrc)
- .filter(pred)
- .map(map);
- let rtok = tokenize_with_text(rsrc)
- .filter(pred)
- .map(map);
+ let ltok = tokenize_with_text(lsrc).filter(pred).map(map);
+ let rtok = tokenize_with_text(rsrc).filter(pred).map(map);
ltok.eq(rtok)
} else {
// Unable to access the source. Conservatively assume the blocks aren't equal.
diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs
index 13da79fba..1181dfc0e 100644
--- a/src/tools/clippy/clippy_utils/src/lib.rs
+++ b/src/tools/clippy/clippy_utils/src/lib.rs
@@ -54,7 +54,6 @@ pub mod higher;
mod hir_utils;
pub mod macros;
pub mod mir;
-pub mod msrvs;
pub mod numeric_literal;
pub mod paths;
pub mod ptr;
@@ -80,7 +79,6 @@ use std::sync::{Mutex, MutexGuard, OnceLock};
use if_chain::if_chain;
use itertools::Itertools;
use rustc_ast::ast::{self, LitKind, RangeLimits};
-use rustc_ast::Attribute;
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::unhash::UnhashMap;
use rustc_hir::def::{DefKind, Res};
@@ -113,7 +111,7 @@ use rustc_span::{sym, Span};
use rustc_target::abi::Integer;
use visitors::Visitable;
-use crate::consts::{constant, miri_to_const, Constant};
+use crate::consts::{constant, mir_to_const, Constant};
use crate::higher::Range;
use crate::ty::{
adt_and_variant_of_res, can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type,
@@ -145,7 +143,7 @@ macro_rules! extract_msrv_attr {
/// instead.
///
/// Examples:
-/// ```
+/// ```no_run
/// let abc = 1;
/// // ^ output
/// let def = abc;
@@ -289,7 +287,7 @@ pub fn is_wild(pat: &Pat<'_>) -> bool {
pub fn is_ty_alias(qpath: &QPath<'_>) -> bool {
match *qpath {
QPath::Resolved(_, path) => matches!(path.res, Res::Def(DefKind::TyAlias | DefKind::AssocTy, ..)),
- QPath::TypeRelative(ty, _) if let TyKind::Path(qpath) = ty.kind => { is_ty_alias(&qpath) },
+ QPath::TypeRelative(ty, _) if let TyKind::Path(qpath) = ty.kind => is_ty_alias(&qpath),
_ => false,
}
}
@@ -700,7 +698,7 @@ pub fn get_trait_def_id(cx: &LateContext<'_>, path: &[&str]) -> Option<DefId> {
///
/// Use this if you want to find the `TraitRef` of the `Add` trait in this example:
///
-/// ```rust
+/// ```no_run
/// struct Point(isize, isize);
///
/// impl std::ops::Add for Point {
@@ -865,8 +863,8 @@ pub fn is_default_equivalent(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
}
fn is_default_equivalent_from(cx: &LateContext<'_>, from_func: &Expr<'_>, arg: &Expr<'_>) -> bool {
- if let ExprKind::Path(QPath::TypeRelative(ty, seg)) = from_func.kind &&
- seg.ident.name == sym::from
+ if let ExprKind::Path(QPath::TypeRelative(ty, seg)) = from_func.kind
+ && seg.ident.name == sym::from
{
match arg.kind {
ExprKind::Lit(hir::Lit {
@@ -875,12 +873,12 @@ fn is_default_equivalent_from(cx: &LateContext<'_>, from_func: &Expr<'_>, arg: &
}) => return sym.is_empty() && is_path_lang_item(cx, ty, LangItem::String),
ExprKind::Array([]) => return is_path_diagnostic_item(cx, ty, sym::Vec),
ExprKind::Repeat(_, ArrayLen::Body(len)) => {
- if let ExprKind::Lit(const_lit) = cx.tcx.hir().body(len.body).value.kind &&
- let LitKind::Int(v, _) = const_lit.node
+ if let ExprKind::Lit(const_lit) = cx.tcx.hir().body(len.body).value.kind
+ && let LitKind::Int(v, _) = const_lit.node
{
- return v == 0 && is_path_diagnostic_item(cx, ty, sym::Vec);
+ return v == 0 && is_path_diagnostic_item(cx, ty, sym::Vec);
}
- }
+ },
_ => (),
}
}
@@ -896,7 +894,7 @@ fn is_default_equivalent_from(cx: &LateContext<'_>, from_func: &Expr<'_>, arg: &
///
/// For example, given the following function:
///
-/// ```
+/// ```no_run
/// fn f<'a>(iter: &mut impl Iterator<Item = (usize, &'a mut String)>) {
/// for item in iter {
/// let s = item.1;
@@ -1509,9 +1507,7 @@ pub fn is_range_full(cx: &LateContext<'_>, expr: &Expr<'_>, container_path: Opti
if let rustc_ty::Adt(_, subst) = ty.kind()
&& let bnd_ty = subst.type_at(0)
&& let Some(min_val) = bnd_ty.numeric_min_val(cx.tcx)
- && let const_val = cx.tcx.valtree_to_const_val((bnd_ty, min_val.to_valtree()))
- && let min_const_kind = Const::from_value(const_val, bnd_ty)
- && let Some(min_const) = miri_to_const(cx, min_const_kind)
+ && let Some(min_const) = mir_to_const(cx, Const::from_ty_const(min_val, cx.tcx))
&& let Some(start_const) = constant(cx, cx.typeck_results(), start)
{
start_const == min_const
@@ -1519,34 +1515,30 @@ pub fn is_range_full(cx: &LateContext<'_>, expr: &Expr<'_>, container_path: Opti
false
}
});
- let end_is_none_or_max = end.map_or(true, |end| {
- match limits {
- RangeLimits::Closed => {
- if let rustc_ty::Adt(_, subst) = ty.kind()
- && let bnd_ty = subst.type_at(0)
- && let Some(max_val) = bnd_ty.numeric_max_val(cx.tcx)
- && let const_val = cx.tcx.valtree_to_const_val((bnd_ty, max_val.to_valtree()))
- && let max_const_kind = Const::from_value(const_val, bnd_ty)
- && let Some(max_const) = miri_to_const(cx, max_const_kind)
- && let Some(end_const) = constant(cx, cx.typeck_results(), end)
- {
- end_const == max_const
- } else {
- false
- }
- },
- RangeLimits::HalfOpen => {
- if let Some(container_path) = container_path
- && let ExprKind::MethodCall(name, self_arg, [], _) = end.kind
- && name.ident.name == sym::len
- && let ExprKind::Path(QPath::Resolved(None, path)) = self_arg.kind
- {
- container_path.res == path.res
- } else {
- false
- }
- },
- }
+ let end_is_none_or_max = end.map_or(true, |end| match limits {
+ RangeLimits::Closed => {
+ if let rustc_ty::Adt(_, subst) = ty.kind()
+ && let bnd_ty = subst.type_at(0)
+ && let Some(max_val) = bnd_ty.numeric_max_val(cx.tcx)
+ && let Some(max_const) = mir_to_const(cx, Const::from_ty_const(max_val, cx.tcx))
+ && let Some(end_const) = constant(cx, cx.typeck_results(), end)
+ {
+ end_const == max_const
+ } else {
+ false
+ }
+ },
+ RangeLimits::HalfOpen => {
+ if let Some(container_path) = container_path
+ && let ExprKind::MethodCall(name, self_arg, [], _) = end.kind
+ && name.ident.name == sym::len
+ && let ExprKind::Path(QPath::Resolved(None, path)) = self_arg.kind
+ {
+ container_path.res == path.res
+ } else {
+ false
+ }
+ },
});
return start_is_none_or_min && end_is_none_or_max;
}
@@ -1614,7 +1606,7 @@ pub fn is_expn_of(mut span: Span, name: &str) -> Option<Span> {
/// Returns the pre-expansion span if the span directly comes from an expansion
/// of the macro `name`.
/// The difference with [`is_expn_of`] is that in
-/// ```rust
+/// ```no_run
/// # macro_rules! foo { ($name:tt!$args:tt) => { $name!$args } }
/// # macro_rules! bar { ($e:expr) => { $e } }
/// foo!(bar!(42));
@@ -2032,51 +2024,101 @@ pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
did.map_or(false, |did| cx.tcx.has_attr(did, sym::must_use))
}
-/// Checks if an expression represents the identity function
-/// Only examines closures and `std::convert::identity`
-pub fn is_expr_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
- /// Checks if a function's body represents the identity function. Looks for bodies of the form:
- /// * `|x| x`
- /// * `|x| return x`
- /// * `|x| { return x }`
- /// * `|x| { return x; }`
- fn is_body_identity_function(cx: &LateContext<'_>, func: &Body<'_>) -> bool {
- let id = if_chain! {
- if let [param] = func.params;
- if let PatKind::Binding(_, id, _, _) = param.pat.kind;
- then {
- id
- } else {
- return false;
- }
- };
+/// Checks if a function's body represents the identity function. Looks for bodies of the form:
+/// * `|x| x`
+/// * `|x| return x`
+/// * `|x| { return x }`
+/// * `|x| { return x; }`
+/// * `|(x, y)| (x, y)`
+///
+/// Consider calling [`is_expr_untyped_identity_function`] or [`is_expr_identity_function`] instead.
+fn is_body_identity_function(cx: &LateContext<'_>, func: &Body<'_>) -> bool {
+ fn check_pat(cx: &LateContext<'_>, pat: &Pat<'_>, expr: &Expr<'_>) -> bool {
+ match (pat.kind, expr.kind) {
+ (PatKind::Binding(_, id, _, _), _) => {
+ path_to_local_id(expr, id) && cx.typeck_results().expr_adjustments(expr).is_empty()
+ },
+ (PatKind::Tuple(pats, dotdot), ExprKind::Tup(tup))
+ if dotdot.as_opt_usize().is_none() && pats.len() == tup.len() =>
+ {
+ pats.iter().zip(tup).all(|(pat, expr)| check_pat(cx, pat, expr))
+ },
+ _ => false,
+ }
+ }
- let mut expr = func.value;
- loop {
- match expr.kind {
- #[rustfmt::skip]
- ExprKind::Block(&Block { stmts: [], expr: Some(e), .. }, _, )
- | ExprKind::Ret(Some(e)) => expr = e,
- #[rustfmt::skip]
- ExprKind::Block(&Block { stmts: [stmt], expr: None, .. }, _) => {
- if_chain! {
- if let StmtKind::Semi(e) | StmtKind::Expr(e) = stmt.kind;
- if let ExprKind::Ret(Some(ret_val)) = e.kind;
- then {
- expr = ret_val;
- } else {
- return false;
- }
- }
+ let [param] = func.params else {
+ return false;
+ };
+
+ let mut expr = func.value;
+ loop {
+ match expr.kind {
+ ExprKind::Block(
+ &Block {
+ stmts: [],
+ expr: Some(e),
+ ..
},
- _ => return path_to_local_id(expr, id) && cx.typeck_results().expr_adjustments(expr).is_empty(),
- }
+ _,
+ )
+ | ExprKind::Ret(Some(e)) => expr = e,
+ ExprKind::Block(
+ &Block {
+ stmts: [stmt],
+ expr: None,
+ ..
+ },
+ _,
+ ) => {
+ if_chain! {
+ if let StmtKind::Semi(e) | StmtKind::Expr(e) = stmt.kind;
+ if let ExprKind::Ret(Some(ret_val)) = e.kind;
+ then {
+ expr = ret_val;
+ } else {
+ return false;
+ }
+ }
+ },
+ _ => return check_pat(cx, param.pat, expr),
}
}
+}
+
+/// This is the same as [`is_expr_identity_function`], but does not consider closures
+/// with type annotations for its bindings (or similar) as identity functions:
+/// * `|x: u8| x`
+/// * `std::convert::identity::<u8>`
+pub fn is_expr_untyped_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
+ match expr.kind {
+ ExprKind::Closure(&Closure { body, fn_decl, .. })
+ if fn_decl.inputs.iter().all(|ty| matches!(ty.kind, TyKind::Infer)) =>
+ {
+ is_body_identity_function(cx, cx.tcx.hir().body(body))
+ },
+ ExprKind::Path(QPath::Resolved(_, path))
+ if path.segments.iter().all(|seg| seg.infer_args)
+ && let Some(did) = path.res.opt_def_id() =>
+ {
+ cx.tcx.is_diagnostic_item(sym::convert_identity, did)
+ },
+ _ => false,
+ }
+}
+/// Checks if an expression represents the identity function
+/// Only examines closures and `std::convert::identity`
+///
+/// NOTE: If you want to use this function to find out if a closure is unnecessary, you likely want
+/// to call [`is_expr_untyped_identity_function`] instead, which makes sure that the closure doesn't
+/// have type annotations. This is important because removing a closure with bindings can
+/// remove type information that helped type inference before, which can then lead to compile
+/// errors.
+pub fn is_expr_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
match expr.kind {
ExprKind::Closure(&Closure { body, .. }) => is_body_identity_function(cx, cx.tcx.hir().body(body)),
- _ => path_def_id(cx, expr).map_or(false, |id| match_def_path(cx, id, &paths::CONVERT_IDENTITY)),
+ _ => path_def_id(cx, expr).map_or(false, |id| cx.tcx.is_diagnostic_item(sym::convert_identity, id)),
}
}
@@ -2160,7 +2202,7 @@ pub fn is_no_core_crate(cx: &LateContext<'_>) -> bool {
/// Check if parent of a hir node is a trait implementation block.
/// For example, `f` in
-/// ```rust
+/// ```no_run
/// # struct S;
/// # trait Trait { fn f(); }
/// impl Trait for S {
@@ -2412,7 +2454,8 @@ fn with_test_item_names(tcx: TyCtxt<'_>, module: LocalModDefId, f: impl Fn(&[Sym
for id in tcx.hir().module_items(module) {
if matches!(tcx.def_kind(id.owner_id), DefKind::Const)
&& let item = tcx.hir().item(id)
- && let ItemKind::Const(ty, _generics, _body) = item.kind {
+ && let ItemKind::Const(ty, _generics, _body) = item.kind
+ {
if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind {
// We could also check for the type name `test::TestDescAndFn`
if let Res::Def(DefKind::Struct, _) = path.res {
@@ -2456,11 +2499,12 @@ pub fn is_in_test_function(tcx: TyCtxt<'_>, id: hir::HirId) -> bool {
})
}
-/// Checks if the item containing the given `HirId` has `#[cfg(test)]` attribute applied
+/// Checks if `id` has a `#[cfg(test)]` attribute applied
///
-/// Note: Add `//@compile-flags: --test` to UI tests with a `#[cfg(test)]` function
-pub fn is_in_cfg_test(tcx: TyCtxt<'_>, id: hir::HirId) -> bool {
- fn is_cfg_test(attr: &Attribute) -> bool {
+/// This only checks directly applied attributes, to see if a node is inside a `#[cfg(test)]` parent
+/// use [`is_in_cfg_test`]
+pub fn is_cfg_test(tcx: TyCtxt<'_>, id: hir::HirId) -> bool {
+ tcx.hir().attrs(id).iter().any(|attr| {
if attr.has_name(sym::cfg)
&& let Some(items) = attr.meta_item_list()
&& let [item] = &*items
@@ -2470,11 +2514,14 @@ pub fn is_in_cfg_test(tcx: TyCtxt<'_>, id: hir::HirId) -> bool {
} else {
false
}
- }
+ })
+}
+
+/// Checks if any parent node of `HirId` has `#[cfg(test)]` attribute applied
+pub fn is_in_cfg_test(tcx: TyCtxt<'_>, id: hir::HirId) -> bool {
tcx.hir()
- .parent_iter(id)
- .flat_map(|(parent_id, _)| tcx.hir().attrs(parent_id))
- .any(is_cfg_test)
+ .parent_id_iter(id)
+ .any(|parent_id| is_cfg_test(tcx, parent_id))
}
/// Checks if the item of any of its parents has `#[cfg(...)]` attribute applied.
@@ -2672,7 +2719,9 @@ pub fn expr_use_ctxt<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> Optio
let ctxt = e.span.ctxt();
walk_to_expr_usage(cx, e, &mut |parent, child_id| {
// LocalTableInContext returns the wrong lifetime, so go use `expr_adjustments` instead.
- if adjustments.is_empty() && let Node::Expr(e) = cx.tcx.hir().get(child_id) {
+ if adjustments.is_empty()
+ && let Node::Expr(e) = cx.tcx.hir().get(child_id)
+ {
adjustments = cx.typeck_results().expr_adjustments(e);
}
match parent {
@@ -2869,13 +2918,13 @@ pub fn pat_and_expr_can_be_question_mark<'a, 'hir>(
pat: &'a Pat<'hir>,
else_body: &Expr<'_>,
) -> Option<&'a Pat<'hir>> {
- if let PatKind::TupleStruct(pat_path, [inner_pat], _) = pat.kind &&
- is_res_lang_ctor(cx, cx.qpath_res(&pat_path, pat.hir_id), OptionSome) &&
- !is_refutable(cx, inner_pat) &&
- let else_body = peel_blocks(else_body) &&
- let ExprKind::Ret(Some(ret_val)) = else_body.kind &&
- let ExprKind::Path(ret_path) = ret_val.kind &&
- is_res_lang_ctor(cx, cx.qpath_res(&ret_path, ret_val.hir_id), OptionNone)
+ if let PatKind::TupleStruct(pat_path, [inner_pat], _) = pat.kind
+ && is_res_lang_ctor(cx, cx.qpath_res(&pat_path, pat.hir_id), OptionSome)
+ && !is_refutable(cx, inner_pat)
+ && let else_body = peel_blocks(else_body)
+ && let ExprKind::Ret(Some(ret_val)) = else_body.kind
+ && let ExprKind::Path(ret_path) = ret_val.kind
+ && is_res_lang_ctor(cx, cx.qpath_res(&ret_path, ret_val.hir_id), OptionNone)
{
Some(inner_pat)
} else {
@@ -2913,3 +2962,15 @@ op_utils! {
Shl ShlAssign
Shr ShrAssign
}
+
+/// Returns `true` if the pattern is a `PatWild`, or is an ident prefixed with `_`
+/// that is not locally used.
+pub fn pat_is_wild<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx PatKind<'_>, body: impl Visitable<'tcx>) -> bool {
+ match *pat {
+ PatKind::Wild => true,
+ PatKind::Binding(_, id, ident, None) if ident.as_str().starts_with('_') => {
+ !visitors::is_local_used(cx, body, id)
+ },
+ _ => false,
+ }
+}
diff --git a/src/tools/clippy/clippy_utils/src/macros.rs b/src/tools/clippy/clippy_utils/src/macros.rs
index 82508bcdb..46ce4ffdc 100644
--- a/src/tools/clippy/clippy_utils/src/macros.rs
+++ b/src/tools/clippy/clippy_utils/src/macros.rs
@@ -228,16 +228,26 @@ pub enum PanicExpn<'a> {
impl<'a> PanicExpn<'a> {
pub fn parse(expr: &'a Expr<'a>) -> Option<Self> {
- let ExprKind::Call(callee, [arg, rest @ ..]) = &expr.kind else {
+ let ExprKind::Call(callee, args) = &expr.kind else {
return None;
};
let ExprKind::Path(QPath::Resolved(_, path)) = &callee.kind else {
return None;
};
- let result = match path.segments.last().unwrap().ident.as_str() {
- "panic" if arg.span.ctxt() == expr.span.ctxt() => Self::Empty,
+ let name = path.segments.last().unwrap().ident.as_str();
+
+ // This has no argument
+ if name == "panic_cold_explicit" {
+ return Some(Self::Empty);
+ };
+
+ let [arg, rest @ ..] = args else {
+ return None;
+ };
+ let result = match name {
+ "panic" if arg.span.eq_ctxt(expr.span) => Self::Empty,
"panic" | "panic_str" => Self::Str(arg),
- "panic_display" => {
+ "panic_display" | "panic_cold_display" => {
let ExprKind::AddrOf(_, _, e) = &arg.kind else {
return None;
};
diff --git a/src/tools/clippy/clippy_utils/src/paths.rs b/src/tools/clippy/clippy_utils/src/paths.rs
index 2fb24b5c7..5bca55437 100644
--- a/src/tools/clippy/clippy_utils/src/paths.rs
+++ b/src/tools/clippy/clippy_utils/src/paths.rs
@@ -4,16 +4,13 @@
//! Whenever possible, please consider diagnostic items over hardcoded paths.
//! See <https://github.com/rust-lang/rust-clippy/issues/5393> for more information.
-#[cfg(feature = "internal")]
pub const APPLICABILITY: [&str; 2] = ["rustc_lint_defs", "Applicability"];
-#[cfg(feature = "internal")]
pub const APPLICABILITY_VALUES: [[&str; 3]; 4] = [
["rustc_lint_defs", "Applicability", "Unspecified"],
["rustc_lint_defs", "Applicability", "HasPlaceholders"],
["rustc_lint_defs", "Applicability", "MaybeIncorrect"],
["rustc_lint_defs", "Applicability", "MachineApplicable"],
];
-#[cfg(feature = "internal")]
pub const DIAGNOSTIC_BUILDER: [&str; 3] = ["rustc_errors", "diagnostic_builder", "DiagnosticBuilder"];
pub const BINARYHEAP_ITER: [&str; 5] = ["alloc", "collections", "binary_heap", "BinaryHeap", "iter"];
pub const BTREEMAP_CONTAINS_KEY: [&str; 6] = ["alloc", "collections", "btree", "map", "BTreeMap", "contains_key"];
@@ -25,17 +22,10 @@ pub const CORE_ITER_COPIED: [&str; 6] = ["core", "iter", "traits", "iterator", "
pub const CORE_ITER_FILTER: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "filter"];
pub const CORE_RESULT_OK_METHOD: [&str; 4] = ["core", "result", "Result", "ok"];
pub const CSTRING_AS_C_STR: [&str; 5] = ["alloc", "ffi", "c_str", "CString", "as_c_str"];
-pub const DEFAULT_TRAIT_METHOD: [&str; 4] = ["core", "default", "Default", "default"];
-pub const DEREF_MUT_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "DerefMut", "deref_mut"];
-#[cfg(feature = "internal")]
pub const EARLY_CONTEXT: [&str; 2] = ["rustc_lint", "EarlyContext"];
-#[cfg(feature = "internal")]
pub const EARLY_LINT_PASS: [&str; 3] = ["rustc_lint", "passes", "EarlyLintPass"];
-pub const EXIT: [&str; 3] = ["std", "process", "exit"];
pub const F32_EPSILON: [&str; 4] = ["core", "f32", "<impl f32>", "EPSILON"];
pub const F64_EPSILON: [&str; 4] = ["core", "f64", "<impl f64>", "EPSILON"];
-pub const FROM_ITERATOR_METHOD: [&str; 6] = ["core", "iter", "traits", "collect", "FromIterator", "from_iter"];
-pub const FROM_STR_METHOD: [&str; 5] = ["core", "str", "traits", "FromStr", "from_str"];
#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates
pub const FUTURES_IO_ASYNCREADEXT: [&str; 3] = ["futures_util", "io", "AsyncReadExt"];
#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates
@@ -43,26 +33,15 @@ pub const FUTURES_IO_ASYNCWRITEEXT: [&str; 3] = ["futures_util", "io", "AsyncWri
pub const HASHMAP_CONTAINS_KEY: [&str; 6] = ["std", "collections", "hash", "map", "HashMap", "contains_key"];
pub const HASHMAP_INSERT: [&str; 6] = ["std", "collections", "hash", "map", "HashMap", "insert"];
pub const HASHSET_ITER: [&str; 6] = ["std", "collections", "hash", "set", "HashSet", "iter"];
-#[cfg(feature = "internal")]
pub const IDENT: [&str; 3] = ["rustc_span", "symbol", "Ident"];
-#[cfg(feature = "internal")]
pub const IDENT_AS_STR: [&str; 4] = ["rustc_span", "symbol", "Ident", "as_str"];
pub const INSERT_STR: [&str; 4] = ["alloc", "string", "String", "insert_str"];
-pub const ITER_EMPTY: [&str; 5] = ["core", "iter", "sources", "empty", "Empty"];
-pub const ITER_ONCE: [&str; 5] = ["core", "iter", "sources", "once", "Once"];
pub const ITERTOOLS_NEXT_TUPLE: [&str; 3] = ["itertools", "Itertools", "next_tuple"];
-#[cfg(feature = "internal")]
pub const KW_MODULE: [&str; 3] = ["rustc_span", "symbol", "kw"];
-#[cfg(feature = "internal")]
pub const LATE_CONTEXT: [&str; 2] = ["rustc_lint", "LateContext"];
-#[cfg(feature = "internal")]
pub const LATE_LINT_PASS: [&str; 3] = ["rustc_lint", "passes", "LateLintPass"];
-#[cfg(feature = "internal")]
pub const LINT: [&str; 2] = ["rustc_lint_defs", "Lint"];
-pub const MEM_SWAP: [&str; 3] = ["core", "mem", "swap"];
-#[cfg(feature = "internal")]
-pub const MSRV: [&str; 3] = ["clippy_utils", "msrvs", "Msrv"];
-pub const OPEN_OPTIONS: [&str; 3] = ["std", "fs", "OpenOptions"];
+pub const MSRV: [&str; 3] = ["clippy_config", "msrvs", "Msrv"];
pub const OS_STRING_AS_OS_STR: [&str; 5] = ["std", "ffi", "os_str", "OsString", "as_os_str"];
pub const OS_STR_TO_OS_STRING: [&str; 5] = ["std", "ffi", "os_str", "OsStr", "to_os_string"];
pub const PARKING_LOT_MUTEX_GUARD: [&str; 3] = ["lock_api", "mutex", "MutexGuard"];
@@ -71,28 +50,9 @@ pub const PARKING_LOT_RWLOCK_WRITE_GUARD: [&str; 3] = ["lock_api", "rwlock", "Rw
pub const PATH_BUF_AS_PATH: [&str; 4] = ["std", "path", "PathBuf", "as_path"];
pub const PATH_MAIN_SEPARATOR: [&str; 3] = ["std", "path", "MAIN_SEPARATOR"];
pub const PATH_TO_PATH_BUF: [&str; 4] = ["std", "path", "Path", "to_path_buf"];
-pub const PEEKABLE: [&str; 5] = ["core", "iter", "adapters", "peekable", "Peekable"];
-pub const PERMISSIONS: [&str; 3] = ["std", "fs", "Permissions"];
#[cfg_attr(not(unix), allow(clippy::invalid_paths))]
pub const PERMISSIONS_FROM_MODE: [&str; 6] = ["std", "os", "unix", "fs", "PermissionsExt", "from_mode"];
-pub const PTR_COPY: [&str; 3] = ["core", "intrinsics", "copy"];
-pub const PTR_COPY_NONOVERLAPPING: [&str; 3] = ["core", "intrinsics", "copy_nonoverlapping"];
-pub const PTR_EQ: [&str; 3] = ["core", "ptr", "eq"];
-pub const PTR_SLICE_FROM_RAW_PARTS: [&str; 3] = ["core", "ptr", "slice_from_raw_parts"];
-pub const PTR_SLICE_FROM_RAW_PARTS_MUT: [&str; 3] = ["core", "ptr", "slice_from_raw_parts_mut"];
-pub const PTR_SWAP_NONOVERLAPPING: [&str; 3] = ["core", "ptr", "swap_nonoverlapping"];
-pub const PTR_READ: [&str; 3] = ["core", "ptr", "read"];
-pub const PTR_READ_UNALIGNED: [&str; 3] = ["core", "ptr", "read_unaligned"];
-pub const PTR_READ_VOLATILE: [&str; 3] = ["core", "ptr", "read_volatile"];
-pub const PTR_REPLACE: [&str; 3] = ["core", "ptr", "replace"];
-pub const PTR_SWAP: [&str; 3] = ["core", "ptr", "swap"];
-pub const PTR_UNALIGNED_VOLATILE_LOAD: [&str; 3] = ["core", "intrinsics", "unaligned_volatile_load"];
-pub const PTR_UNALIGNED_VOLATILE_STORE: [&str; 3] = ["core", "intrinsics", "unaligned_volatile_store"];
-pub const PTR_WRITE_BYTES: [&str; 3] = ["core", "intrinsics", "write_bytes"];
pub const PUSH_STR: [&str; 4] = ["alloc", "string", "String", "push_str"];
-pub const RANGE_ARGUMENT_TRAIT: [&str; 3] = ["core", "ops", "RangeBounds"];
-pub const REFCELL_REF: [&str; 3] = ["core", "cell", "Ref"];
-pub const REFCELL_REFMUT: [&str; 3] = ["core", "cell", "RefMut"];
pub const REGEX_BUILDER_NEW: [&str; 3] = ["regex", "RegexBuilder", "new"];
pub const REGEX_BYTES_BUILDER_NEW: [&str; 4] = ["regex", "bytes", "RegexBuilder", "new"];
pub const REGEX_BYTES_NEW: [&str; 4] = ["regex", "bytes", "Regex", "new"];
@@ -101,21 +61,11 @@ pub const REGEX_NEW: [&str; 3] = ["regex", "Regex", "new"];
pub const REGEX_SET_NEW: [&str; 3] = ["regex", "RegexSet", "new"];
pub const SERDE_DESERIALIZE: [&str; 3] = ["serde", "de", "Deserialize"];
pub const SERDE_DE_VISITOR: [&str; 3] = ["serde", "de", "Visitor"];
-pub const SLICE_FROM_RAW_PARTS: [&str; 4] = ["core", "slice", "raw", "from_raw_parts"];
-pub const SLICE_FROM_RAW_PARTS_MUT: [&str; 4] = ["core", "slice", "raw", "from_raw_parts_mut"];
pub const SLICE_GET: [&str; 4] = ["core", "slice", "<impl [T]>", "get"];
pub const SLICE_INTO_VEC: [&str; 4] = ["alloc", "slice", "<impl [T]>", "into_vec"];
pub const SLICE_INTO: [&str; 4] = ["core", "slice", "<impl [T]>", "iter"];
-pub const SLICE_ITER: [&str; 4] = ["core", "slice", "iter", "Iter"];
-pub const STDERR: [&str; 4] = ["std", "io", "stdio", "stderr"];
-pub const STDOUT: [&str; 4] = ["std", "io", "stdio", "stdout"];
-pub const CONVERT_IDENTITY: [&str; 3] = ["core", "convert", "identity"];
-pub const STD_FS_CREATE_DIR: [&str; 3] = ["std", "fs", "create_dir"];
-pub const STD_IO_LINES: [&str; 3] = ["std", "io", "Lines"];
-pub const STD_IO_SEEK: [&str; 3] = ["std", "io", "Seek"];
pub const STD_IO_SEEK_FROM_CURRENT: [&str; 4] = ["std", "io", "SeekFrom", "Current"];
pub const STD_IO_SEEKFROM_START: [&str; 4] = ["std", "io", "SeekFrom", "Start"];
-pub const STD_PROCESS_COMMAND: [&str; 3] = ["std", "process", "Command"];
pub const STRING_AS_MUT_STR: [&str; 4] = ["alloc", "string", "String", "as_mut_str"];
pub const STRING_AS_STR: [&str; 4] = ["alloc", "string", "String", "as_str"];
pub const STRING_NEW: [&str; 4] = ["alloc", "string", "String", "new"];
@@ -124,25 +74,16 @@ pub const STR_CHARS: [&str; 4] = ["core", "str", "<impl str>", "chars"];
pub const STR_ENDS_WITH: [&str; 4] = ["core", "str", "<impl str>", "ends_with"];
pub const STR_LEN: [&str; 4] = ["core", "str", "<impl str>", "len"];
pub const STR_STARTS_WITH: [&str; 4] = ["core", "str", "<impl str>", "starts_with"];
-#[cfg(feature = "internal")]
pub const SYMBOL: [&str; 3] = ["rustc_span", "symbol", "Symbol"];
-#[cfg(feature = "internal")]
pub const SYMBOL_AS_STR: [&str; 4] = ["rustc_span", "symbol", "Symbol", "as_str"];
-#[cfg(feature = "internal")]
pub const SYMBOL_INTERN: [&str; 4] = ["rustc_span", "symbol", "Symbol", "intern"];
-#[cfg(feature = "internal")]
pub const SYMBOL_TO_IDENT_STRING: [&str; 4] = ["rustc_span", "symbol", "Symbol", "to_ident_string"];
-#[cfg(feature = "internal")]
pub const SYM_MODULE: [&str; 3] = ["rustc_span", "symbol", "sym"];
-#[cfg(feature = "internal")]
pub const SYNTAX_CONTEXT: [&str; 3] = ["rustc_span", "hygiene", "SyntaxContext"];
-pub const TO_OWNED_METHOD: [&str; 4] = ["alloc", "borrow", "ToOwned", "to_owned"];
-pub const TO_STRING_METHOD: [&str; 4] = ["alloc", "string", "ToString", "to_string"];
#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates
pub const TOKIO_IO_ASYNCREADEXT: [&str; 5] = ["tokio", "io", "util", "async_read_ext", "AsyncReadExt"];
#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates
pub const TOKIO_IO_ASYNCWRITEEXT: [&str; 5] = ["tokio", "io", "util", "async_write_ext", "AsyncWriteExt"];
-pub const TRY_FROM: [&str; 4] = ["core", "convert", "TryFrom", "try_from"];
pub const VEC_AS_MUT_SLICE: [&str; 4] = ["alloc", "vec", "Vec", "as_mut_slice"];
pub const VEC_AS_SLICE: [&str; 4] = ["alloc", "vec", "Vec", "as_slice"];
pub const VEC_DEQUE_ITER: [&str; 5] = ["alloc", "collections", "vec_deque", "VecDeque", "iter"];
@@ -150,18 +91,11 @@ pub const VEC_FROM_ELEM: [&str; 3] = ["alloc", "vec", "from_elem"];
pub const VEC_NEW: [&str; 4] = ["alloc", "vec", "Vec", "new"];
pub const VEC_WITH_CAPACITY: [&str; 4] = ["alloc", "vec", "Vec", "with_capacity"];
pub const VEC_RESIZE: [&str; 4] = ["alloc", "vec", "Vec", "resize"];
-pub const WEAK_ARC: [&str; 3] = ["alloc", "sync", "Weak"];
-pub const WEAK_RC: [&str; 3] = ["alloc", "rc", "Weak"];
-pub const PTR_NON_NULL: [&str; 4] = ["core", "ptr", "non_null", "NonNull"];
pub const INSTANT_NOW: [&str; 4] = ["std", "time", "Instant", "now"];
-pub const INSTANT: [&str; 3] = ["std", "time", "Instant"];
pub const VEC_IS_EMPTY: [&str; 4] = ["alloc", "vec", "Vec", "is_empty"];
pub const VEC_POP: [&str; 4] = ["alloc", "vec", "Vec", "pop"];
+pub const WAKER: [&str; 4] = ["core", "task", "wake", "Waker"];
pub const OPTION_UNWRAP: [&str; 4] = ["core", "option", "Option", "unwrap"];
pub const OPTION_EXPECT: [&str; 4] = ["core", "option", "Option", "expect"];
-pub const FORMATTER: [&str; 3] = ["core", "fmt", "Formatter"];
-pub const DEBUG_STRUCT: [&str; 4] = ["core", "fmt", "builders", "DebugStruct"];
-pub const ORD_CMP: [&str; 4] = ["core", "cmp", "Ord", "cmp"];
#[expect(clippy::invalid_paths)] // not sure why it thinks this, it works so
pub const BOOL_THEN: [&str; 4] = ["core", "bool", "<impl bool>", "then"];
-pub const ARRAY_INTO_ITER: [&str; 4] = ["core", "array", "iter", "IntoIter"];
diff --git a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs
index 55f9cb27a..668ea9fcf 100644
--- a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs
+++ b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs
@@ -3,8 +3,9 @@
// of terminologies might not be relevant in the context of Clippy. Note that its behavior might
// differ from the time of `rustc` even if the name stays the same.
-use crate::msrvs::Msrv;
+use clippy_config::msrvs::Msrv;
use hir::LangItem;
+use rustc_attr::StableSince;
use rustc_const_eval::transform::check_consts::ConstCx;
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
@@ -305,8 +306,8 @@ fn check_terminator<'tcx>(
Ok(())
},
TerminatorKind::SwitchInt { discr, targets: _ } => check_operand(tcx, discr, span, body),
- TerminatorKind::GeneratorDrop | TerminatorKind::Yield { .. } => {
- Err((span, "const fn generators are unstable".into()))
+ TerminatorKind::CoroutineDrop | TerminatorKind::Yield { .. } => {
+ Err((span, "const fn coroutines are unstable".into()))
},
TerminatorKind::Call {
func,
@@ -370,19 +371,17 @@ fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: &Msrv) -> bool {
// function could be removed if `rustc` provided a MSRV-aware version of `is_const_fn`.
// as a part of an unimplemented MSRV check https://github.com/rust-lang/rust/issues/65262.
- // HACK(nilstrieb): CURRENT_RUSTC_VERSION can return versions like 1.66.0-dev. `rustc-semver`
- // doesn't accept the `-dev` version number so we have to strip it off.
- let short_version = since
- .as_str()
- .split('-')
- .next()
- .expect("rustc_attr::StabilityLevel::Stable::since` is empty");
+ let const_stab_rust_version = match since {
+ StableSince::Version(version) => version,
+ StableSince::Current => rustc_session::RustcVersion::CURRENT,
+ StableSince::Err => return false,
+ };
- let since = rustc_span::Symbol::intern(short_version);
-
- msrv.meets(RustcVersion::parse(since.as_str()).unwrap_or_else(|err| {
- panic!("`rustc_attr::StabilityLevel::Stable::since` is ill-formatted: `{since}`, {err:?}")
- }))
+ msrv.meets(RustcVersion::new(
+ u32::from(const_stab_rust_version.major),
+ u32::from(const_stab_rust_version.minor),
+ u32::from(const_stab_rust_version.patch),
+ ))
} else {
// Unstable const fn with the feature enabled.
msrv.current().is_none()
diff --git a/src/tools/clippy/clippy_utils/src/source.rs b/src/tools/clippy/clippy_utils/src/source.rs
index 31cb42109..e72467ede 100644
--- a/src/tools/clippy/clippy_utils/src/source.rs
+++ b/src/tools/clippy/clippy_utils/src/source.rs
@@ -70,9 +70,9 @@ pub fn expr_block<T: LintContext>(
app: &mut Applicability,
) -> String {
let (code, from_macro) = snippet_block_with_context(cx, expr.span, outer, default, indent_relative_to, app);
- if !from_macro &&
- let ExprKind::Block(block, _) = expr.kind &&
- block.rules != BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided)
+ if !from_macro
+ && let ExprKind::Block(block, _) = expr.kind
+ && block.rules != BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided)
{
format!("{code}")
} else {
@@ -108,7 +108,7 @@ fn first_char_in_first_line<T: LintContext>(cx: &T, span: Span) -> Option<BytePo
/// Extends the span to the beginning of the spans line, incl. whitespaces.
///
-/// ```rust
+/// ```no_run
/// let x = ();
/// // ^^
/// // will be converted to
diff --git a/src/tools/clippy/clippy_utils/src/str_utils.rs b/src/tools/clippy/clippy_utils/src/str_utils.rs
index 03a9d3c25..421b25a77 100644
--- a/src/tools/clippy/clippy_utils/src/str_utils.rs
+++ b/src/tools/clippy/clippy_utils/src/str_utils.rs
@@ -14,7 +14,7 @@ impl StrIndex {
/// Returns the index of the character after the first camel-case component of `s`.
///
-/// ```
+/// ```no_run
/// # use clippy_utils::str_utils::{camel_case_until, StrIndex};
/// assert_eq!(camel_case_until("AbcDef"), StrIndex::new(6, 6));
/// assert_eq!(camel_case_until("ABCD"), StrIndex::new(0, 0));
@@ -58,7 +58,7 @@ pub fn camel_case_until(s: &str) -> StrIndex {
/// Returns index of the first camel-case component of `s`.
///
-/// ```
+/// ```no_run
/// # use clippy_utils::str_utils::{camel_case_start, StrIndex};
/// assert_eq!(camel_case_start("AbcDef"), StrIndex::new(0, 0));
/// assert_eq!(camel_case_start("abcDef"), StrIndex::new(3, 3));
@@ -73,7 +73,7 @@ pub fn camel_case_start(s: &str) -> StrIndex {
/// Returns `StrIndex` of the last camel-case component of `s[idx..]`.
///
-/// ```
+/// ```no_run
/// # use clippy_utils::str_utils::{camel_case_start_from_idx, StrIndex};
/// assert_eq!(camel_case_start_from_idx("AbcDef", 0), StrIndex::new(0, 0));
/// assert_eq!(camel_case_start_from_idx("AbcDef", 1), StrIndex::new(3, 3));
@@ -122,7 +122,7 @@ pub fn camel_case_start_from_idx(s: &str, start_idx: usize) -> StrIndex {
/// Get the indexes of camel case components of a string `s`
///
-/// ```
+/// ```no_run
/// # use clippy_utils::str_utils::{camel_case_indices, StrIndex};
/// assert_eq!(
/// camel_case_indices("AbcDef"),
@@ -149,7 +149,7 @@ pub fn camel_case_indices(s: &str) -> Vec<StrIndex> {
/// Split camel case string into a vector of its components
///
-/// ```
+/// ```no_run
/// # use clippy_utils::str_utils::{camel_case_split, StrIndex};
/// assert_eq!(camel_case_split("AbcDef"), vec!["Abc", "Def"]);
/// ```
@@ -181,7 +181,7 @@ impl StrCount {
/// Returns the number of chars that match from the start
///
-/// ```
+/// ```no_run
/// # use clippy_utils::str_utils::{count_match_start, StrCount};
/// assert_eq!(count_match_start("hello_mouse", "hello_penguin"), StrCount::new(6, 6));
/// assert_eq!(count_match_start("hello_clippy", "bye_bugs"), StrCount::new(0, 0));
@@ -207,7 +207,7 @@ pub fn count_match_start(str1: &str, str2: &str) -> StrCount {
/// Returns the number of chars and bytes that match from the end
///
-/// ```
+/// ```no_run
/// # use clippy_utils::str_utils::{count_match_end, StrCount};
/// assert_eq!(count_match_end("hello_cat", "bye_cat"), StrCount::new(4, 4));
/// assert_eq!(count_match_end("if_item_thing", "enum_value"), StrCount::new(0, 0));
@@ -236,6 +236,59 @@ pub fn count_match_end(str1: &str, str2: &str) -> StrCount {
})
}
+/// Returns a `snake_case` version of the input
+/// ```no_run
+/// use clippy_utils::str_utils::to_snake_case;
+/// assert_eq!(to_snake_case("AbcDef"), "abc_def");
+/// assert_eq!(to_snake_case("ABCD"), "a_b_c_d");
+/// assert_eq!(to_snake_case("AbcDD"), "abc_d_d");
+/// assert_eq!(to_snake_case("Abc1DD"), "abc1_d_d");
+/// ```
+pub fn to_snake_case(name: &str) -> String {
+ let mut s = String::new();
+ for (i, c) in name.chars().enumerate() {
+ if c.is_uppercase() {
+ // characters without capitalization are considered lowercase
+ if i != 0 {
+ s.push('_');
+ }
+ s.extend(c.to_lowercase());
+ } else {
+ s.push(c);
+ }
+ }
+ s
+}
+/// Returns a `CamelCase` version of the input
+/// ```no_run
+/// use clippy_utils::str_utils::to_camel_case;
+/// assert_eq!(to_camel_case("abc_def"), "AbcDef");
+/// assert_eq!(to_camel_case("a_b_c_d"), "ABCD");
+/// assert_eq!(to_camel_case("abc_d_d"), "AbcDD");
+/// assert_eq!(to_camel_case("abc1_d_d"), "Abc1DD");
+/// ```
+pub fn to_camel_case(item_name: &str) -> String {
+ let mut s = String::new();
+ let mut up = true;
+ for c in item_name.chars() {
+ if c.is_uppercase() {
+ // we only turn snake case text into CamelCase
+ return item_name.to_string();
+ }
+ if c == '_' {
+ up = true;
+ continue;
+ }
+ if up {
+ up = false;
+ s.extend(c.to_uppercase());
+ } else {
+ s.push(c);
+ }
+ }
+ s
+}
+
#[cfg(test)]
mod test {
use super::*;
diff --git a/src/tools/clippy/clippy_utils/src/sugg.rs b/src/tools/clippy/clippy_utils/src/sugg.rs
index ae8ee371f..db79dd788 100644
--- a/src/tools/clippy/clippy_utils/src/sugg.rs
+++ b/src/tools/clippy/clippy_utils/src/sugg.rs
@@ -16,7 +16,7 @@ use rustc_lint::{EarlyContext, LateContext, LintContext};
use rustc_middle::hir::place::ProjectionKind;
use rustc_middle::mir::{FakeReadCause, Mutability};
use rustc_middle::ty;
-use rustc_span::source_map::{BytePos, CharPos, Pos, Span, SyntaxContext};
+use rustc_span::{BytePos, CharPos, Pos, Span, SyntaxContext};
use std::borrow::Cow;
use std::fmt::{self, Display, Write as _};
use std::ops::{Add, Neg, Not, Sub};
@@ -190,7 +190,7 @@ impl<'a> Sugg<'a> {
(snip, false) => Sugg::MaybeParen(snip),
(snip, true) => Sugg::NonParen(snip),
},
- ast::ExprKind::Async(..)
+ ast::ExprKind::Gen(..)
| ast::ExprKind::Block(..)
| ast::ExprKind::Break(..)
| ast::ExprKind::Call(..)
@@ -465,7 +465,10 @@ forward_binop_impls_to_ref!(impl Sub, sub for Sugg<'_>, type Output = Sugg<'stat
impl Neg for Sugg<'_> {
type Output = Sugg<'static>;
fn neg(self) -> Sugg<'static> {
- make_unop("-", self)
+ match &self {
+ Self::BinOp(AssocOp::As, ..) => Sugg::MaybeParen(format!("-({self})").into()),
+ _ => make_unop("-", self),
+ }
}
}
diff --git a/src/tools/clippy/clippy_utils/src/ty.rs b/src/tools/clippy/clippy_utils/src/ty.rs
index 604dc7691..842a206f9 100644
--- a/src/tools/clippy/clippy_utils/src/ty.rs
+++ b/src/tools/clippy/clippy_utils/src/ty.rs
@@ -31,7 +31,7 @@ use rustc_trait_selection::traits::{Obligation, ObligationCause};
use std::assert_matches::debug_assert_matches;
use std::iter;
-use crate::{match_def_path, path_res, paths};
+use crate::{match_def_path, path_res};
mod type_certainty;
pub use type_certainty::expr_type_is_certain;
@@ -461,10 +461,8 @@ pub fn needs_ordered_drop<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
else if is_type_lang_item(cx, ty, LangItem::OwnedBox)
|| matches!(
get_type_diagnostic_name(cx, ty),
- Some(sym::HashSet | sym::Rc | sym::Arc | sym::cstring_type)
+ Some(sym::HashSet | sym::Rc | sym::Arc | sym::cstring_type | sym::RcWeak | sym::ArcWeak)
)
- || match_type(cx, ty, &paths::WEAK_RC)
- || match_type(cx, ty, &paths::WEAK_ARC)
{
// Check all of the generic arguments.
if let ty::Adt(_, subs) = ty.kind() {
@@ -892,7 +890,9 @@ pub fn for_each_top_level_late_bound_region<B>(
impl<'tcx, B, F: FnMut(BoundRegion) -> ControlFlow<B>> TypeVisitor<TyCtxt<'tcx>> for V<F> {
type BreakTy = B;
fn visit_region(&mut self, r: Region<'tcx>) -> ControlFlow<Self::BreakTy> {
- if let RegionKind::ReLateBound(idx, bound) = r.kind() && idx.as_u32() == self.index {
+ if let RegionKind::ReLateBound(idx, bound) = r.kind()
+ && idx.as_u32() == self.index
+ {
(self.f)(bound)
} else {
ControlFlow::Continue(())
@@ -986,16 +986,16 @@ pub fn ty_is_fn_once_param<'tcx>(tcx: TyCtxt<'_>, ty: Ty<'tcx>, predicates: &'tc
.iter()
.try_fold(false, |found, p| {
if let ty::ClauseKind::Trait(p) = p.kind().skip_binder()
- && let ty::Param(self_ty) = p.trait_ref.self_ty().kind()
- && ty.index == self_ty.index
- {
- // This should use `super_traits_of`, but that's a private function.
- if p.trait_ref.def_id == fn_once_id {
- return Some(true);
- } else if p.trait_ref.def_id == fn_mut_id || p.trait_ref.def_id == fn_id {
- return None;
+ && let ty::Param(self_ty) = p.trait_ref.self_ty().kind()
+ && ty.index == self_ty.index
+ {
+ // This should use `super_traits_of`, but that's a private function.
+ if p.trait_ref.def_id == fn_once_id {
+ return Some(true);
+ } else if p.trait_ref.def_id == fn_mut_id || p.trait_ref.def_id == fn_id {
+ return None;
+ }
}
- }
Some(found)
})
.unwrap_or(false)
@@ -1135,7 +1135,7 @@ pub fn make_projection<'tcx>(
#[cfg(debug_assertions)]
assert_generic_args_match(tcx, assoc_item.def_id, args);
- Some(tcx.mk_alias_ty(assoc_item.def_id, args))
+ Some(ty::AliasTy::new(tcx, assoc_item.def_id, args))
}
helper(
tcx,
@@ -1160,7 +1160,12 @@ pub fn make_normalized_projection<'tcx>(
) -> Option<Ty<'tcx>> {
fn helper<'tcx>(tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, ty: AliasTy<'tcx>) -> Option<Ty<'tcx>> {
#[cfg(debug_assertions)]
- if let Some((i, arg)) = ty.args.iter().enumerate().find(|(_, arg)| arg.has_late_bound_regions()) {
+ if let Some((i, arg)) = ty
+ .args
+ .iter()
+ .enumerate()
+ .find(|(_, arg)| arg.has_escaping_bound_vars())
+ {
debug_assert!(
false,
"args contain late-bound region at index `{i}` which can't be normalized.\n\
@@ -1233,7 +1238,12 @@ pub fn make_normalized_projection_with_regions<'tcx>(
) -> Option<Ty<'tcx>> {
fn helper<'tcx>(tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, ty: AliasTy<'tcx>) -> Option<Ty<'tcx>> {
#[cfg(debug_assertions)]
- if let Some((i, arg)) = ty.args.iter().enumerate().find(|(_, arg)| arg.has_late_bound_regions()) {
+ if let Some((i, arg)) = ty
+ .args
+ .iter()
+ .enumerate()
+ .find(|(_, arg)| arg.has_escaping_bound_vars())
+ {
debug_assert!(
false,
"args contain late-bound region at index `{i}` which can't be normalized.\n\
diff --git a/src/tools/clippy/clippy_utils/src/ty/type_certainty/mod.rs b/src/tools/clippy/clippy_utils/src/ty/type_certainty/mod.rs
index 750646723..76fa15e15 100644
--- a/src/tools/clippy/clippy_utils/src/ty/type_certainty/mod.rs
+++ b/src/tools/clippy/clippy_utils/src/ty/type_certainty/mod.rs
@@ -267,7 +267,9 @@ fn path_segment_certainty(
/// For at least some `QPath::TypeRelative`, the path segment's `res` can be `Res::Err`.
/// `update_res` tries to fix the resolution when `parent_certainty` is `Certain(Some(..))`.
fn update_res(cx: &LateContext<'_>, parent_certainty: Certainty, path_segment: &PathSegment<'_>) -> Option<Res> {
- if path_segment.res == Res::Err && let Some(def_id) = parent_certainty.to_def_id() {
+ if path_segment.res == Res::Err
+ && let Some(def_id) = parent_certainty.to_def_id()
+ {
let mut def_path = cx.get_def_path(def_id);
def_path.push(path_segment.ident.name);
let reses = def_path_res(cx, &def_path.iter().map(Symbol::as_str).collect::<Vec<_>>());
diff --git a/src/tools/clippy/clippy_utils/src/visitors.rs b/src/tools/clippy/clippy_utils/src/visitors.rs
index 3b47a4513..d752fe7d9 100644
--- a/src/tools/clippy/clippy_utils/src/visitors.rs
+++ b/src/tools/clippy/clippy_utils/src/visitors.rs
@@ -62,6 +62,27 @@ where
}
}
}
+impl<'tcx, A, B> Visitable<'tcx> for (A, B)
+where
+ A: Visitable<'tcx>,
+ B: Visitable<'tcx>,
+{
+ fn visit<V: Visitor<'tcx>>(self, visitor: &mut V) {
+ let (a, b) = self;
+ a.visit(visitor);
+ b.visit(visitor);
+ }
+}
+impl<'tcx, T> Visitable<'tcx> for Option<T>
+where
+ T: Visitable<'tcx>,
+{
+ fn visit<V: Visitor<'tcx>>(self, visitor: &mut V) {
+ if let Some(x) = self {
+ x.visit(visitor);
+ }
+ }
+}
macro_rules! visitable_ref {
($t:ident, $f:ident) => {
impl<'tcx> Visitable<'tcx> for &'tcx $t<'tcx> {
@@ -748,3 +769,26 @@ pub fn contains_break_or_continue(expr: &Expr<'_>) -> bool {
})
.is_some()
}
+
+/// If the local is only used once in `visitable` returns the path expression referencing the given
+/// local
+pub fn local_used_once<'tcx>(
+ cx: &LateContext<'tcx>,
+ visitable: impl Visitable<'tcx>,
+ id: HirId,
+) -> Option<&'tcx Expr<'tcx>> {
+ let mut expr = None;
+
+ let cf = for_each_expr_with_closures(cx, visitable, |e| {
+ if path_to_local_id(e, id) && expr.replace(e).is_some() {
+ ControlFlow::Break(())
+ } else {
+ ControlFlow::Continue(())
+ }
+ });
+ if cf.is_some() {
+ return None;
+ }
+
+ expr
+}
diff --git a/src/tools/clippy/declare_clippy_lint/Cargo.toml b/src/tools/clippy/declare_clippy_lint/Cargo.toml
index 1470da61f..beea9fd00 100644
--- a/src/tools/clippy/declare_clippy_lint/Cargo.toml
+++ b/src/tools/clippy/declare_clippy_lint/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "declare_clippy_lint"
-version = "0.1.74"
+version = "0.1.75"
edition = "2021"
publish = false
diff --git a/src/tools/clippy/declare_clippy_lint/src/lib.rs b/src/tools/clippy/declare_clippy_lint/src/lib.rs
index 0057256f6..dc3037f66 100644
--- a/src/tools/clippy/declare_clippy_lint/src/lib.rs
+++ b/src/tools/clippy/declare_clippy_lint/src/lib.rs
@@ -94,7 +94,7 @@ impl Parse for ClippyLint {
///
/// # Example
///
-/// ```
+/// ```ignore
/// use rustc_session::declare_tool_lint;
///
/// declare_clippy_lint! {
@@ -136,28 +136,16 @@ pub fn declare_clippy_lint(input: TokenStream) -> TokenStream {
"{}",
match category.as_str() {
"correctness" => "Deny",
- "style" | "suspicious" | "complexity" | "perf" | "internal_warn" => "Warn",
+ "style" | "suspicious" | "complexity" | "perf" => "Warn",
"pedantic" | "restriction" | "cargo" | "nursery" | "internal" => "Allow",
_ => panic!("unknown category {category}"),
},
);
- let info = if category == "internal_warn" {
- None
- } else {
- let info_name = format_ident!("{name}_INFO");
-
- (&mut category[0..1]).make_ascii_uppercase();
- let category_variant = format_ident!("{category}");
+ let info_name = format_ident!("{name}_INFO");
- Some(quote! {
- pub(crate) static #info_name: &'static crate::LintInfo = &crate::LintInfo {
- lint: &#name,
- category: crate::LintCategory::#category_variant,
- explanation: #explanation,
- };
- })
- };
+ (&mut category[0..1]).make_ascii_uppercase();
+ let category_variant = format_ident!("{category}");
let output = quote! {
declare_tool_lint! {
@@ -168,7 +156,11 @@ pub fn declare_clippy_lint(input: TokenStream) -> TokenStream {
report_in_external_macro: true
}
- #info
+ pub(crate) static #info_name: &'static crate::LintInfo = &crate::LintInfo {
+ lint: &#name,
+ category: crate::LintCategory::#category_variant,
+ explanation: #explanation,
+ };
};
TokenStream::from(output)
diff --git a/src/tools/clippy/lintcheck/src/main.rs b/src/tools/clippy/lintcheck/src/main.rs
index 3a022b343..58cb42316 100644
--- a/src/tools/clippy/lintcheck/src/main.rs
+++ b/src/tools/clippy/lintcheck/src/main.rs
@@ -523,7 +523,7 @@ fn gather_stats(clippy_warnings: &[ClippyWarning]) -> (String, HashMap<&String,
.for_each(|wrn| *counter.entry(&wrn.lint_type).or_insert(0) += 1);
// collect into a tupled list for sorting
- let mut stats: Vec<(&&String, &usize)> = counter.iter().map(|(lint, count)| (lint, count)).collect();
+ let mut stats: Vec<(&&String, &usize)> = counter.iter().collect();
// sort by "000{count} {clippy::lintname}"
// to not have a lint with 200 and 2 warnings take the same spot
stats.sort_by_key(|(lint, count)| format!("{count:0>4}, {lint}"));
diff --git a/src/tools/clippy/rust-toolchain b/src/tools/clippy/rust-toolchain
index 5ce22b65f..293fcbf39 100644
--- a/src/tools/clippy/rust-toolchain
+++ b/src/tools/clippy/rust-toolchain
@@ -1,3 +1,3 @@
[toolchain]
-channel = "nightly-2023-09-25"
+channel = "nightly-2023-11-02"
components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"]
diff --git a/src/tools/clippy/src/driver.rs b/src/tools/clippy/src/driver.rs
index 1d89477dc..7bb49d08d 100644
--- a/src/tools/clippy/src/driver.rs
+++ b/src/tools/clippy/src/driver.rs
@@ -26,6 +26,8 @@ use std::ops::Deref;
use std::path::Path;
use std::process::exit;
+use anstream::println;
+
/// If a command-line option matches `find_arg`, then apply the predicate `pred` on its value. If
/// true, then return it. The parameter is assumed to be either `--arg=value` or `--arg value`.
fn arg_value<'a, T: Deref<Target = str>>(
@@ -124,7 +126,7 @@ impl rustc_driver::Callbacks for ClippyCallbacks {
// JUSTIFICATION: necessary in clippy driver to set `mir_opt_level`
#[allow(rustc::bad_opt_access)]
fn config(&mut self, config: &mut interface::Config) {
- let conf_path = clippy_lints::lookup_conf_file();
+ let conf_path = clippy_config::lookup_conf_file();
let previous = config.register_lints.take();
let clippy_args_var = self.clippy_args_var.take();
config.parse_sess_created = Some(Box::new(move |parse_sess| {
@@ -145,9 +147,9 @@ impl rustc_driver::Callbacks for ClippyCallbacks {
(previous)(sess, lint_store);
}
- let conf = clippy_lints::read_conf(sess, &conf_path);
- clippy_lints::register_plugins(lint_store, sess, &conf);
- clippy_lints::register_pre_expansion_lints(lint_store, sess, &conf);
+ let conf = clippy_config::Conf::read(sess, &conf_path);
+ clippy_lints::register_plugins(lint_store, sess, conf);
+ clippy_lints::register_pre_expansion_lints(lint_store, conf);
clippy_lints::register_renamed(lint_store);
}));
@@ -162,45 +164,21 @@ impl rustc_driver::Callbacks for ClippyCallbacks {
}
}
+#[allow(clippy::ignored_unit_patterns)]
fn display_help() {
- println!(
- "\
-Checks a package to catch common mistakes and improve your Rust code.
-
-Usage:
- cargo clippy [options] [--] [<opts>...]
-
-Common options:
- -h, --help Print this message
- --rustc Pass all args to rustc
- -V, --version Print version info and exit
-
-For the other options see `cargo check --help`.
-
-To allow or deny a lint from the command line you can use `cargo clippy --`
-with:
-
- -W --warn OPT Set lint warnings
- -A --allow OPT Set lint allowed
- -D --deny OPT Set lint denied
- -F --forbid OPT Set lint forbidden
-
-You can use tool lints to allow or deny lints from your code, eg.:
-
- #[allow(clippy::needless_lifetimes)]
-"
- );
+ println!("{}", help_message());
}
const BUG_REPORT_URL: &str = "https://github.com/rust-lang/rust-clippy/issues/new?template=ice.yml";
#[allow(clippy::too_many_lines)]
+#[allow(clippy::ignored_unit_patterns)]
pub fn main() {
let handler = EarlyErrorHandler::new(ErrorOutputType::default());
rustc_driver::init_rustc_env_logger(&handler);
- rustc_driver::install_ice_hook(BUG_REPORT_URL, |handler| {
+ let using_internal_features = rustc_driver::install_ice_hook(BUG_REPORT_URL, |handler| {
// FIXME: this macro calls unwrap internally but is called in a panicking context! It's not
// as simple as moving the call from the hook to main, because `install_ice_hook` doesn't
// accept a generic closure.
@@ -236,6 +214,7 @@ pub fn main() {
if orig_args.iter().any(|a| a == "--version" || a == "-V") {
let version_info = rustc_tools_util::get_version_info!();
+
println!("{version_info}");
exit(0);
}
@@ -286,9 +265,35 @@ pub fn main() {
let clippy_enabled = !cap_lints_allow && (!no_deps || in_primary_package);
if clippy_enabled {
args.extend(clippy_args);
- rustc_driver::RunCompiler::new(&args, &mut ClippyCallbacks { clippy_args_var }).run()
+ rustc_driver::RunCompiler::new(&args, &mut ClippyCallbacks { clippy_args_var })
+ .set_using_internal_features(using_internal_features)
+ .run()
} else {
- rustc_driver::RunCompiler::new(&args, &mut RustcCallbacks { clippy_args_var }).run()
+ rustc_driver::RunCompiler::new(&args, &mut RustcCallbacks { clippy_args_var })
+ .set_using_internal_features(using_internal_features)
+ .run()
}
}))
}
+
+#[must_use]
+fn help_message() -> &'static str {
+ color_print::cstr!(
+ "Checks a file to catch common mistakes and improve your Rust code.
+Run <cyan>clippy-driver</> with the same arguments you use for <cyan>rustc</>
+
+<green,bold>Usage</>:
+ <cyan,bold>clippy-driver</> <cyan>[OPTIONS] INPUT</>
+
+<green,bold>Common options:</>
+ <cyan,bold>-h</>, <cyan,bold>--help</> Print this message
+ <cyan,bold>-V</>, <cyan,bold>--version</> Print version info and exit
+ <cyan,bold>--rustc</> Pass all arguments to <cyan>rustc</>
+
+<green,bold>Allowing / Denying lints</>
+You can use tool lints to allow or deny lints from your code, e.g.:
+
+ <yellow,bold>#[allow(clippy::needless_lifetimes)]</>
+"
+ )
+}
diff --git a/src/tools/clippy/src/main.rs b/src/tools/clippy/src/main.rs
index 26b655076..bbf7d22c8 100644
--- a/src/tools/clippy/src/main.rs
+++ b/src/tools/clippy/src/main.rs
@@ -6,37 +6,14 @@ use std::env;
use std::path::PathBuf;
use std::process::{self, Command};
-const CARGO_CLIPPY_HELP: &str = "Checks a package to catch common mistakes and improve your Rust code.
-
-Usage:
- cargo clippy [options] [--] [<opts>...]
-
-Common options:
- --no-deps Run Clippy only on the given crate, without linting the dependencies
- --fix Automatically apply lint suggestions. This flag implies `--no-deps` and `--all-targets`
- -h, --help Print this message
- -V, --version Print version info and exit
- --explain LINT Print the documentation for a given lint
-
-For the other options see `cargo check --help`.
-
-To allow or deny a lint from the command line you can use `cargo clippy --`
-with:
-
- -W --warn OPT Set lint warnings
- -A --allow OPT Set lint allowed
- -D --deny OPT Set lint denied
- -F --forbid OPT Set lint forbidden
-
-You can use tool lints to allow or deny lints from your code, e.g.:
-
- #[allow(clippy::needless_lifetimes)]
-";
+use anstream::println;
+#[allow(clippy::ignored_unit_patterns)]
fn show_help() {
- println!("{CARGO_CLIPPY_HELP}");
+ println!("{}", help_message());
}
+#[allow(clippy::ignored_unit_patterns)]
fn show_version() {
let version_info = rustc_tools_util::get_version_info!();
println!("{version_info}");
@@ -168,6 +145,38 @@ where
}
}
+#[must_use]
+pub fn help_message() -> &'static str {
+ color_print::cstr!(
+"Checks a package to catch common mistakes and improve your Rust code.
+
+<green,bold>Usage</>:
+ <cyan,bold>cargo clippy</> <cyan>[OPTIONS] [--] [<<ARGS>>...]</>
+
+<green,bold>Common options:</>
+ <cyan,bold>--no-deps</> Run Clippy only on the given crate, without linting the dependencies
+ <cyan,bold>--fix</> Automatically apply lint suggestions. This flag implies <cyan>--no-deps</> and <cyan>--all-targets</>
+ <cyan,bold>-h</>, <cyan,bold>--help</> Print this message
+ <cyan,bold>-V</>, <cyan,bold>--version</> Print version info and exit
+ <cyan,bold>--explain [LINT]</> Print the documentation for a given lint
+
+See all options with <cyan,bold>cargo check --help</>.
+
+<green,bold>Allowing / Denying lints</>
+
+To allow or deny a lint from the command line you can use <cyan,bold>cargo clippy --</> with:
+
+ <cyan,bold>-W</> / <cyan,bold>--warn</> <cyan>[LINT]</> Set lint warnings
+ <cyan,bold>-A</> / <cyan,bold>--allow</> <cyan>[LINT]</> Set lint allowed
+ <cyan,bold>-D</> / <cyan,bold>--deny</> <cyan>[LINT]</> Set lint denied
+ <cyan,bold>-F</> / <cyan,bold>--forbid</> <cyan>[LINT]</> Set lint forbidden
+
+You can use tool lints to allow or deny lints from your code, e.g.:
+
+ <yellow,bold>#[allow(clippy::needless_lifetimes)]</>
+"
+ )
+}
#[cfg(test)]
mod tests {
use super::ClippyCmd;
diff --git a/src/tools/clippy/tests/compile-test.rs b/src/tools/clippy/tests/compile-test.rs
index f340cf593..3b7c974b6 100644
--- a/src/tools/clippy/tests/compile-test.rs
+++ b/src/tools/clippy/tests/compile-test.rs
@@ -30,6 +30,7 @@ mod test_utils;
/// All crates used in UI tests are listed here
static TEST_DEPENDENCIES: &[&str] = &[
+ "clippy_config",
"clippy_lints",
"clippy_utils",
"futures",
@@ -105,27 +106,20 @@ static EXTERN_FLAGS: LazyLock<Vec<String>> = LazyLock::new(|| {
// whether to run internal tests or not
const RUN_INTERNAL_TESTS: bool = cfg!(feature = "internal");
-fn canonicalize(path: impl AsRef<Path>) -> PathBuf {
- let path = path.as_ref();
- fs::create_dir_all(path).unwrap();
- fs::canonicalize(path).unwrap_or_else(|err| panic!("{} cannot be canonicalized: {err}", path.display()))
-}
-
fn base_config(test_dir: &str) -> (Config, Args) {
let mut args = Args::test().unwrap();
args.bless |= var_os("RUSTC_BLESS").is_some_and(|v| v != "0");
+ let target_dir = PathBuf::from(var_os("CARGO_TARGET_DIR").unwrap_or_else(|| "target".into()));
let mut config = Config {
mode: Mode::Yolo {
rustfix: ui_test::RustfixMode::Everything,
},
- stderr_filters: vec![(Match::PathBackslash, b"/")],
- stdout_filters: vec![],
filter_files: env::var("TESTNAME")
.map(|filters| filters.split(',').map(str::to_string).collect())
.unwrap_or_default(),
target: None,
- out_dir: canonicalize(var_os("CARGO_TARGET_DIR").unwrap_or_else(|| "target".into())).join("ui_test"),
+ out_dir: target_dir.join("ui_test"),
..Config::rustc(Path::new("tests").join(test_dir))
};
config.with_args(&args, /* bless by default */ false);
@@ -168,19 +162,13 @@ fn run_ui() {
config
.program
.envs
- .push(("CLIPPY_CONF_DIR".into(), Some(canonicalize("tests").into())));
-
- let quiet = args.quiet;
+ .push(("CLIPPY_CONF_DIR".into(), Some("tests".into())));
ui_test::run_tests_generic(
vec![config],
ui_test::default_file_filter,
ui_test::default_per_file_config,
- if quiet {
- status_emitter::Text::quiet()
- } else {
- status_emitter::Text::verbose()
- },
+ status_emitter::Text::from(args.format),
)
.unwrap();
}
@@ -194,17 +182,12 @@ fn run_internal_tests() {
if let OutputConflictHandling::Error(err) = &mut config.output_conflict_handling {
*err = "cargo uitest --features internal -- -- --bless".into();
}
- let quiet = args.quiet;
ui_test::run_tests_generic(
vec![config],
ui_test::default_file_filter,
ui_test::default_per_file_config,
- if quiet {
- status_emitter::Text::quiet()
- } else {
- status_emitter::Text::verbose()
- },
+ status_emitter::Text::from(args.format),
)
.unwrap();
}
@@ -212,22 +195,9 @@ fn run_internal_tests() {
fn run_ui_toml() {
let (mut config, args) = base_config("ui-toml");
- config.stderr_filters = vec![
- (
- Match::Exact(
- canonicalize("tests")
- .parent()
- .unwrap()
- .to_string_lossy()
- .as_bytes()
- .to_vec(),
- ),
- b"$DIR",
- ),
- (Match::Exact(b"\\".to_vec()), b"/"),
- ];
-
- let quiet = args.quiet;
+ config
+ .stderr_filters
+ .push((Match::from(env::current_dir().unwrap().as_path()), b"$DIR"));
ui_test::run_tests_generic(
vec![config],
@@ -238,11 +208,7 @@ fn run_ui_toml() {
.envs
.push(("CLIPPY_CONF_DIR".into(), Some(path.parent().unwrap().into())));
},
- if quiet {
- status_emitter::Text::quiet()
- } else {
- status_emitter::Text::verbose()
- },
+ status_emitter::Text::from(args.format),
)
.unwrap();
}
@@ -270,22 +236,9 @@ fn run_ui_cargo() {
});
config.edition = None;
- config.stderr_filters = vec![
- (
- Match::Exact(
- canonicalize("tests")
- .parent()
- .unwrap()
- .to_string_lossy()
- .as_bytes()
- .to_vec(),
- ),
- b"$DIR",
- ),
- (Match::Exact(b"\\".to_vec()), b"/"),
- ];
-
- let quiet = args.quiet;
+ config
+ .stderr_filters
+ .push((Match::from(env::current_dir().unwrap().as_path()), b"$DIR"));
let ignored_32bit = |path: &Path| {
// FIXME: for some reason the modules are linted in a different order for this test
@@ -297,20 +250,8 @@ fn run_ui_cargo() {
|path, config| {
path.ends_with("Cargo.toml") && ui_test::default_any_file_filter(path, config) && !ignored_32bit(path)
},
- |config, path, _file_contents| {
- config.out_dir = canonicalize(
- std::env::current_dir()
- .unwrap()
- .join("target")
- .join("ui_test_cargo/")
- .join(path.parent().unwrap()),
- );
- },
- if quiet {
- status_emitter::Text::quiet()
- } else {
- status_emitter::Text::verbose()
- },
+ |_config, _path, _file_contents| {},
+ status_emitter::Text::from(args.format),
)
.unwrap();
}
diff --git a/src/tools/clippy/tests/dogfood.rs b/src/tools/clippy/tests/dogfood.rs
index afde31fac..3f16c180e 100644
--- a/src/tools/clippy/tests/dogfood.rs
+++ b/src/tools/clippy/tests/dogfood.rs
@@ -28,6 +28,7 @@ fn dogfood_clippy() {
"clippy_dev",
"clippy_lints",
"clippy_utils",
+ "clippy_config",
"lintcheck",
"rustc_tools_util",
] {
@@ -56,7 +57,10 @@ fn run_metadata_collection_lint() {
// Run collection as is
std::env::set_var("ENABLE_METADATA_COLLECTION", "1");
- run_clippy_for_package("clippy_lints", &["-A", "unfulfilled_lint_expectations"]);
+ assert!(run_clippy_for_package(
+ "clippy_lints",
+ &["-A", "unfulfilled_lint_expectations"]
+ ));
// Check if cargo caching got in the way
if let Ok(file) = File::open(metadata_output_path) {
@@ -79,9 +83,13 @@ fn run_metadata_collection_lint() {
.unwrap();
// Running the collection again
- run_clippy_for_package("clippy_lints", &["-A", "unfulfilled_lint_expectations"]);
+ assert!(run_clippy_for_package(
+ "clippy_lints",
+ &["-A", "unfulfilled_lint_expectations"]
+ ));
}
+#[must_use]
fn run_clippy_for_package(project: &str, args: &[&str]) -> bool {
let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
diff --git a/src/tools/clippy/tests/ui-internal/invalid_msrv_attr_impl.fixed b/src/tools/clippy/tests/ui-internal/invalid_msrv_attr_impl.fixed
index 928596d08..9b5bf736f 100644
--- a/src/tools/clippy/tests/ui-internal/invalid_msrv_attr_impl.fixed
+++ b/src/tools/clippy/tests/ui-internal/invalid_msrv_attr_impl.fixed
@@ -8,8 +8,8 @@ extern crate rustc_lint;
extern crate rustc_middle;
#[macro_use]
extern crate rustc_session;
+use clippy_config::msrvs::Msrv;
use clippy_utils::extract_msrv_attr;
-use clippy_utils::msrvs::Msrv;
use rustc_hir::Expr;
use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass};
diff --git a/src/tools/clippy/tests/ui-internal/invalid_msrv_attr_impl.rs b/src/tools/clippy/tests/ui-internal/invalid_msrv_attr_impl.rs
index 50b28648c..c5bde47e4 100644
--- a/src/tools/clippy/tests/ui-internal/invalid_msrv_attr_impl.rs
+++ b/src/tools/clippy/tests/ui-internal/invalid_msrv_attr_impl.rs
@@ -8,8 +8,8 @@ extern crate rustc_lint;
extern crate rustc_middle;
#[macro_use]
extern crate rustc_session;
+use clippy_config::msrvs::Msrv;
use clippy_utils::extract_msrv_attr;
-use clippy_utils::msrvs::Msrv;
use rustc_hir::Expr;
use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass};
diff --git a/src/tools/clippy/tests/ui-internal/unnecessary_def_path_hardcoded_path.rs b/src/tools/clippy/tests/ui-internal/unnecessary_def_path_hardcoded_path.rs
index b5ff3a542..f6abb3cc3 100644
--- a/src/tools/clippy/tests/ui-internal/unnecessary_def_path_hardcoded_path.rs
+++ b/src/tools/clippy/tests/ui-internal/unnecessary_def_path_hardcoded_path.rs
@@ -11,6 +11,6 @@ fn main() {
const DEREF_MUT_TRAIT: [&str; 4] = ["core", "ops", "deref", "DerefMut"];
const DEREF_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "Deref", "deref"];
- // Don't lint, not yet a diagnostic or language item
- const DEREF_MUT_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "DerefMut", "deref_mut"];
+ // Don't lint, not a diagnostic or language item
+ const OPS_MOD: [&str; 2] = ["core", "ops"];
}
diff --git a/src/tools/clippy/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr b/src/tools/clippy/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr
index 58b1fd92b..076786329 100644
--- a/src/tools/clippy/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr
+++ b/src/tools/clippy/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr
@@ -19,8 +19,8 @@ LL | const DEREF_MUT_TRAIT: [&str; 4] = ["core", "ops", "deref", "DerefMut"]
error: hardcoded path to a diagnostic item
--> $DIR/unnecessary_def_path_hardcoded_path.rs:12:43
|
-LL | const DEREF_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "Deref", "deref"];
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | const OPS_MOD: [&str; 5] = ["core", "ops"];
+ | ^^^^^^^^^^^^^^^
|
= help: convert all references to use `sym::deref_method`
diff --git a/src/tools/clippy/tests/ui-toml/borrow_interior_mutable_const/clippy.toml b/src/tools/clippy/tests/ui-toml/borrow_interior_mutable_const/clippy.toml
new file mode 100644
index 000000000..34a1036e8
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/borrow_interior_mutable_const/clippy.toml
@@ -0,0 +1 @@
+ignore-interior-mutability = ["borrow_interior_mutable_const_ignore::Counted"] \ No newline at end of file
diff --git a/src/tools/clippy/tests/ui-toml/borrow_interior_mutable_const/ignore.rs b/src/tools/clippy/tests/ui-toml/borrow_interior_mutable_const/ignore.rs
new file mode 100644
index 000000000..79c7cef6c
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/borrow_interior_mutable_const/ignore.rs
@@ -0,0 +1,37 @@
+//@compile-flags: --crate-name borrow_interior_mutable_const_ignore
+
+#![warn(clippy::borrow_interior_mutable_const)]
+#![allow(clippy::declare_interior_mutable_const)]
+
+use core::cell::Cell;
+use std::cmp::{Eq, PartialEq};
+use std::collections::{HashMap, HashSet};
+use std::hash::{Hash, Hasher};
+use std::ops::Deref;
+use std::sync::atomic::{AtomicUsize, Ordering};
+
+struct Counted<T> {
+ count: AtomicUsize,
+ val: T,
+}
+
+impl<T> Counted<T> {
+ const fn new(val: T) -> Self {
+ Self {
+ count: AtomicUsize::new(0),
+ val,
+ }
+ }
+}
+
+enum OptionalCell {
+ Unfrozen(Counted<bool>),
+ Frozen,
+}
+
+const UNFROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Counted::new(true));
+const FROZEN_VARIANT: OptionalCell = OptionalCell::Frozen;
+
+fn main() {
+ let _ = &UNFROZEN_VARIANT;
+}
diff --git a/src/tools/clippy/tests/ui-toml/declare_interior_mutable_const/clippy.toml b/src/tools/clippy/tests/ui-toml/declare_interior_mutable_const/clippy.toml
new file mode 100644
index 000000000..71d13212e
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/declare_interior_mutable_const/clippy.toml
@@ -0,0 +1 @@
+ignore-interior-mutability = ["declare_interior_mutable_const_ignore::Counted"] \ No newline at end of file
diff --git a/src/tools/clippy/tests/ui-toml/declare_interior_mutable_const/ignore.rs b/src/tools/clippy/tests/ui-toml/declare_interior_mutable_const/ignore.rs
new file mode 100644
index 000000000..6385cf4f8
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/declare_interior_mutable_const/ignore.rs
@@ -0,0 +1,46 @@
+//@compile-flags: --crate-name declare_interior_mutable_const_ignore
+
+#![warn(clippy::declare_interior_mutable_const)]
+#![allow(clippy::borrow_interior_mutable_const)]
+
+use core::cell::Cell;
+use std::cmp::{Eq, PartialEq};
+use std::collections::{HashMap, HashSet};
+use std::hash::{Hash, Hasher};
+use std::ops::Deref;
+use std::sync::atomic::{AtomicUsize, Ordering};
+
+struct Counted<T> {
+ count: AtomicUsize,
+ val: T,
+}
+
+impl<T> Counted<T> {
+ const fn new(val: T) -> Self {
+ Self {
+ count: AtomicUsize::new(0),
+ val,
+ }
+ }
+}
+
+enum OptionalCell {
+ Unfrozen(Counted<bool>),
+ Frozen,
+}
+
+const UNFROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Counted::new(true));
+const FROZEN_VARIANT: OptionalCell = OptionalCell::Frozen;
+
+const fn unfrozen_variant() -> OptionalCell {
+ OptionalCell::Unfrozen(Counted::new(true))
+}
+
+const fn frozen_variant() -> OptionalCell {
+ OptionalCell::Frozen
+}
+
+const UNFROZEN_VARIANT_FROM_FN: OptionalCell = unfrozen_variant();
+const FROZEN_VARIANT_FROM_FN: OptionalCell = frozen_variant();
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui-toml/enum_variant_names/enum_variant_names.rs b/src/tools/clippy/tests/ui-toml/enum_variant_names/enum_variant_names.rs
deleted file mode 100644
index 8f4e178cc..000000000
--- a/src/tools/clippy/tests/ui-toml/enum_variant_names/enum_variant_names.rs
+++ /dev/null
@@ -1,16 +0,0 @@
-enum Foo {
- AFoo,
- BFoo,
- CFoo,
- DFoo,
-}
-enum Foo2 {
- //~^ ERROR: all variants have the same postfix
- AFoo,
- BFoo,
- CFoo,
- DFoo,
- EFoo,
-}
-
-fn main() {}
diff --git a/src/tools/clippy/tests/ui-toml/enum_variant_names/enum_variant_names.stderr b/src/tools/clippy/tests/ui-toml/enum_variant_names/enum_variant_names.stderr
deleted file mode 100644
index 11039b1db..000000000
--- a/src/tools/clippy/tests/ui-toml/enum_variant_names/enum_variant_names.stderr
+++ /dev/null
@@ -1,18 +0,0 @@
-error: all variants have the same postfix: `Foo`
- --> $DIR/enum_variant_names.rs:7:1
- |
-LL | / enum Foo2 {
-LL | |
-LL | | AFoo,
-LL | | BFoo,
-... |
-LL | | EFoo,
-LL | | }
- | |_^
- |
- = help: remove the postfixes and use full paths to the variants instead of glob imports
- = note: `-D clippy::enum-variant-names` implied by `-D warnings`
- = help: to override `-D warnings` add `#[allow(clippy::enum_variant_names)]`
-
-error: aborting due to previous error
-
diff --git a/src/tools/clippy/tests/ui-toml/impl_trait_in_params/clippy.toml b/src/tools/clippy/tests/ui-toml/impl_trait_in_params/clippy.toml
new file mode 100644
index 000000000..87e1f2357
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/impl_trait_in_params/clippy.toml
@@ -0,0 +1 @@
+avoid-breaking-exported-api = false \ No newline at end of file
diff --git a/src/tools/clippy/tests/ui-toml/impl_trait_in_params/impl_trait_in_params.rs b/src/tools/clippy/tests/ui-toml/impl_trait_in_params/impl_trait_in_params.rs
new file mode 100644
index 000000000..08fc7edf1
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/impl_trait_in_params/impl_trait_in_params.rs
@@ -0,0 +1,16 @@
+//! As avoid-breaking-exported-api is `false`, nothing here should lint
+#![warn(clippy::impl_trait_in_params)]
+#![no_main]
+//@no-rustfix
+
+pub trait Trait {}
+
+trait Private {
+ fn t(_: impl Trait);
+ fn tt<T: Trait>(_: T);
+}
+
+pub trait Public {
+ fn t(_: impl Trait); //~ ERROR: `impl Trait` used as a function parameter
+ fn tt<T: Trait>(_: T);
+}
diff --git a/src/tools/clippy/tests/ui-toml/impl_trait_in_params/impl_trait_in_params.stderr b/src/tools/clippy/tests/ui-toml/impl_trait_in_params/impl_trait_in_params.stderr
new file mode 100644
index 000000000..80c4f5ed4
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/impl_trait_in_params/impl_trait_in_params.stderr
@@ -0,0 +1,15 @@
+error: `impl Trait` used as a function parameter
+ --> $DIR/impl_trait_in_params.rs:14:13
+ |
+LL | fn t(_: impl Trait);
+ | ^^^^^^^^^^
+ |
+ = note: `-D clippy::impl-trait-in-params` implied by `-D warnings`
+ = help: to override `-D warnings` add `#[allow(clippy::impl_trait_in_params)]`
+help: add a type parameter
+ |
+LL | fn t<{ /* Generic name */ }: Trait>(_: impl Trait);
+ | +++++++++++++++++++++++++++++++
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui-toml/invalid_min_rust_version/invalid_min_rust_version.rs b/src/tools/clippy/tests/ui-toml/invalid_min_rust_version/invalid_min_rust_version.rs
index 03fa71997..85e2fb8c7 100644
--- a/src/tools/clippy/tests/ui-toml/invalid_min_rust_version/invalid_min_rust_version.rs
+++ b/src/tools/clippy/tests/ui-toml/invalid_min_rust_version/invalid_min_rust_version.rs
@@ -1,4 +1,4 @@
-//@error-in-other-file: `invalid.version` is not a valid Rust version
+//@error-in-other-file: not a valid Rust version
#![allow(clippy::redundant_clone)]
diff --git a/src/tools/clippy/tests/ui-toml/invalid_min_rust_version/invalid_min_rust_version.stderr b/src/tools/clippy/tests/ui-toml/invalid_min_rust_version/invalid_min_rust_version.stderr
index e9d8fd2e0..f127c2408 100644
--- a/src/tools/clippy/tests/ui-toml/invalid_min_rust_version/invalid_min_rust_version.stderr
+++ b/src/tools/clippy/tests/ui-toml/invalid_min_rust_version/invalid_min_rust_version.stderr
@@ -1,4 +1,8 @@
-error: error reading Clippy's configuration file. `invalid.version` is not a valid Rust version
+error: error reading Clippy's configuration file: not a valid Rust version
+ --> $DIR/$DIR/clippy.toml:1:8
+ |
+LL | msrv = "invalid.version"
+ | ^^^^^^^^^^^^^^^^^
error: aborting due to previous error
diff --git a/src/tools/clippy/tests/ui-toml/enum_variants_threshold0/clippy.toml b/src/tools/clippy/tests/ui-toml/item_name_repetitions/threshold0/clippy.toml
index f85aade6a..d41edbaf7 100644
--- a/src/tools/clippy/tests/ui-toml/enum_variants_threshold0/clippy.toml
+++ b/src/tools/clippy/tests/ui-toml/item_name_repetitions/threshold0/clippy.toml
@@ -1 +1,2 @@
+struct-field-name-threshold = 0
enum-variant-name-threshold = 0
diff --git a/src/tools/clippy/tests/ui-toml/enum_variants_threshold0/enum_variants_name_threshold.rs b/src/tools/clippy/tests/ui-toml/item_name_repetitions/threshold0/item_name_repetitions.rs
index 6918d7528..b633dcbd1 100644
--- a/src/tools/clippy/tests/ui-toml/enum_variants_threshold0/enum_variants_name_threshold.rs
+++ b/src/tools/clippy/tests/ui-toml/item_name_repetitions/threshold0/item_name_repetitions.rs
@@ -1,3 +1,5 @@
+struct Data {}
+
enum Actions {}
fn main() {}
diff --git a/src/tools/clippy/tests/ui-toml/enum_variant_names/clippy.toml b/src/tools/clippy/tests/ui-toml/item_name_repetitions/threshold5/clippy.toml
index 0ad7a9799..028a62790 100644
--- a/src/tools/clippy/tests/ui-toml/enum_variant_names/clippy.toml
+++ b/src/tools/clippy/tests/ui-toml/item_name_repetitions/threshold5/clippy.toml
@@ -1 +1,2 @@
enum-variant-name-threshold = 5
+struct-field-name-threshold = 5
diff --git a/src/tools/clippy/tests/ui-toml/item_name_repetitions/threshold5/item_name_repetitions.rs b/src/tools/clippy/tests/ui-toml/item_name_repetitions/threshold5/item_name_repetitions.rs
new file mode 100644
index 000000000..d43731169
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/item_name_repetitions/threshold5/item_name_repetitions.rs
@@ -0,0 +1,32 @@
+#![warn(clippy::struct_field_names)]
+
+struct Data {
+ a_data: u8,
+ b_data: u8,
+ c_data: u8,
+ d_data: u8,
+}
+struct Data2 {
+ //~^ ERROR: all fields have the same postfix
+ a_data: u8,
+ b_data: u8,
+ c_data: u8,
+ d_data: u8,
+ e_data: u8,
+}
+enum Foo {
+ AFoo,
+ BFoo,
+ CFoo,
+ DFoo,
+}
+enum Foo2 {
+ //~^ ERROR: all variants have the same postfix
+ AFoo,
+ BFoo,
+ CFoo,
+ DFoo,
+ EFoo,
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui-toml/item_name_repetitions/threshold5/item_name_repetitions.stderr b/src/tools/clippy/tests/ui-toml/item_name_repetitions/threshold5/item_name_repetitions.stderr
new file mode 100644
index 000000000..33802c44b
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/item_name_repetitions/threshold5/item_name_repetitions.stderr
@@ -0,0 +1,34 @@
+error: all fields have the same postfix: `data`
+ --> $DIR/item_name_repetitions.rs:9:1
+ |
+LL | / struct Data2 {
+LL | |
+LL | | a_data: u8,
+LL | | b_data: u8,
+... |
+LL | | e_data: u8,
+LL | | }
+ | |_^
+ |
+ = help: remove the postfixes
+ = note: `-D clippy::struct-field-names` implied by `-D warnings`
+ = help: to override `-D warnings` add `#[allow(clippy::struct_field_names)]`
+
+error: all variants have the same postfix: `Foo`
+ --> $DIR/item_name_repetitions.rs:23:1
+ |
+LL | / enum Foo2 {
+LL | |
+LL | | AFoo,
+LL | | BFoo,
+... |
+LL | | EFoo,
+LL | | }
+ | |_^
+ |
+ = help: remove the postfixes and use full paths to the variants instead of glob imports
+ = note: `-D clippy::enum-variant-names` implied by `-D warnings`
+ = help: to override `-D warnings` add `#[allow(clippy::enum_variant_names)]`
+
+error: aborting due to 2 previous errors
+
diff --git a/src/tools/clippy/tests/ui-toml/pub_crate_missing_docs/pub_crate_missing_doc.rs b/src/tools/clippy/tests/ui-toml/pub_crate_missing_docs/pub_crate_missing_doc.rs
index 830d71f61..cd53f5044 100644
--- a/src/tools/clippy/tests/ui-toml/pub_crate_missing_docs/pub_crate_missing_doc.rs
+++ b/src/tools/clippy/tests/ui-toml/pub_crate_missing_docs/pub_crate_missing_doc.rs
@@ -1,5 +1,6 @@
//! this is crate
#![allow(missing_docs)]
+#![allow(clippy::struct_field_names)]
#![warn(clippy::missing_docs_in_private_items)]
/// this is mod
diff --git a/src/tools/clippy/tests/ui-toml/pub_crate_missing_docs/pub_crate_missing_doc.stderr b/src/tools/clippy/tests/ui-toml/pub_crate_missing_docs/pub_crate_missing_doc.stderr
index 1ecdabbc0..2cf20b460 100644
--- a/src/tools/clippy/tests/ui-toml/pub_crate_missing_docs/pub_crate_missing_doc.stderr
+++ b/src/tools/clippy/tests/ui-toml/pub_crate_missing_docs/pub_crate_missing_doc.stderr
@@ -1,5 +1,5 @@
error: missing documentation for a function
- --> $DIR/pub_crate_missing_doc.rs:12:5
+ --> $DIR/pub_crate_missing_doc.rs:13:5
|
LL | pub(crate) fn crate_no_docs() {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -8,25 +8,25 @@ LL | pub(crate) fn crate_no_docs() {}
= help: to override `-D warnings` add `#[allow(clippy::missing_docs_in_private_items)]`
error: missing documentation for a function
- --> $DIR/pub_crate_missing_doc.rs:15:5
+ --> $DIR/pub_crate_missing_doc.rs:16:5
|
LL | pub(super) fn super_no_docs() {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: missing documentation for a function
- --> $DIR/pub_crate_missing_doc.rs:23:9
+ --> $DIR/pub_crate_missing_doc.rs:24:9
|
LL | pub(crate) fn sub_crate_no_docs() {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: missing documentation for a struct field
- --> $DIR/pub_crate_missing_doc.rs:33:9
+ --> $DIR/pub_crate_missing_doc.rs:34:9
|
LL | pub(crate) crate_field_no_docs: (),
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: missing documentation for a struct
- --> $DIR/pub_crate_missing_doc.rs:39:5
+ --> $DIR/pub_crate_missing_doc.rs:40:5
|
LL | / pub(crate) struct CrateStructNoDocs {
LL | | /// some docs
@@ -38,13 +38,13 @@ LL | | }
| |_____^
error: missing documentation for a struct field
- --> $DIR/pub_crate_missing_doc.rs:42:9
+ --> $DIR/pub_crate_missing_doc.rs:43:9
|
LL | pub(crate) crate_field_no_docs: (),
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: missing documentation for a type alias
- --> $DIR/pub_crate_missing_doc.rs:51:1
+ --> $DIR/pub_crate_missing_doc.rs:52:1
|
LL | type CrateTypedefNoDocs = String;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr
index 4bed5c149..2f9eaa517 100644
--- a/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr
+++ b/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr
@@ -53,6 +53,7 @@ error: error reading Clippy's configuration file: unknown field `foobar`, expect
single-char-binding-names-threshold
stack-size-threshold
standard-macro-braces
+ struct-field-name-threshold
suppress-restriction-lint-in-const
third-party
too-large-for-stack
@@ -126,6 +127,7 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect
single-char-binding-names-threshold
stack-size-threshold
standard-macro-braces
+ struct-field-name-threshold
suppress-restriction-lint-in-const
third-party
too-large-for-stack
diff --git a/src/tools/clippy/tests/ui-toml/too_many_arguments/too_many_arguments.stderr b/src/tools/clippy/tests/ui-toml/too_many_arguments/too_many_arguments.stderr
index a52e1fcb9..8b9d159b5 100644
--- a/src/tools/clippy/tests/ui-toml/too_many_arguments/too_many_arguments.stderr
+++ b/src/tools/clippy/tests/ui-toml/too_many_arguments/too_many_arguments.stderr
@@ -2,7 +2,7 @@ error: this function has too many arguments (11/10)
--> $DIR/too_many_arguments.rs:4:1
|
LL | fn too_many(p1: u8, p2: u8, p3: u8, p4: u8, p5: u8, p6: u8, p7: u8, p8: u8, p9: u8, p10: u8, p11: u8) {}
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `-D clippy::too-many-arguments` implied by `-D warnings`
= help: to override `-D warnings` add `#[allow(clippy::too_many_arguments)]`
diff --git a/src/tools/clippy/tests/ui/author/blocks.stdout b/src/tools/clippy/tests/ui/author/blocks.stdout
index eb3e5189c..140300a16 100644
--- a/src/tools/clippy/tests/ui/author/blocks.stdout
+++ b/src/tools/clippy/tests/ui/author/blocks.stdout
@@ -40,10 +40,10 @@ if let ExprKind::Block(block, None) = expr.kind
{
// report your lint here
}
-if let ExprKind::Closure(CaptureBy::Value, fn_decl, body_id, _, None) = expr.kind
+if let ExprKind::Closure(CaptureBy::Value { .. }, fn_decl, body_id, _, None) = expr.kind
&& let FnRetTy::DefaultReturn(_) = fn_decl.output
&& expr1 = &cx.tcx.hir().body(body_id).value
- && let ExprKind::Closure(CaptureBy::Value, fn_decl1, body_id1, _, Some(Movability::Static)) = expr1.kind
+ && let ExprKind::Closure(CaptureBy::Value { .. }, fn_decl1, body_id1, _, Some(Movability::Static)) = expr1.kind
&& let FnRetTy::DefaultReturn(_) = fn_decl1.output
&& expr2 = &cx.tcx.hir().body(body_id1).value
&& let ExprKind::Block(block, None) = expr2.kind
diff --git a/src/tools/clippy/tests/ui/author/macro_in_closure.rs b/src/tools/clippy/tests/ui/author/macro_in_closure.rs
new file mode 100644
index 000000000..444e6a121
--- /dev/null
+++ b/src/tools/clippy/tests/ui/author/macro_in_closure.rs
@@ -0,0 +1,5 @@
+fn main() {
+ #[clippy::author]
+ let print_text = |x| println!("{}", x);
+ print_text("hello");
+}
diff --git a/src/tools/clippy/tests/ui/author/macro_in_closure.stdout b/src/tools/clippy/tests/ui/author/macro_in_closure.stdout
new file mode 100644
index 000000000..9ab71986f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/author/macro_in_closure.stdout
@@ -0,0 +1,39 @@
+if let StmtKind::Local(local) = stmt.kind
+ && let Some(init) = local.init
+ && let ExprKind::Closure(CaptureBy::Ref, fn_decl, body_id, _, None) = init.kind
+ && let FnRetTy::DefaultReturn(_) = fn_decl.output
+ && expr = &cx.tcx.hir().body(body_id).value
+ && let ExprKind::Block(block, None) = expr.kind
+ && block.stmts.len() == 1
+ && let StmtKind::Semi(e) = block.stmts[0].kind
+ && let ExprKind::Call(func, args) = e.kind
+ && let ExprKind::Path(ref qpath) = func.kind
+ && match_qpath(qpath, &["$crate", "io", "_print"])
+ && args.len() == 1
+ && let ExprKind::Call(func1, args1) = args[0].kind
+ && let ExprKind::Path(ref qpath1) = func1.kind
+ && args1.len() == 2
+ && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner) = args1[0].kind
+ && let ExprKind::Array(elements) = inner.kind
+ && elements.len() == 2
+ && let ExprKind::Lit(ref lit) = elements[0].kind
+ && let LitKind::Str(s, _) = lit.node
+ && s.as_str() == ""
+ && let ExprKind::Lit(ref lit1) = elements[1].kind
+ && let LitKind::Str(s1, _) = lit1.node
+ && s1.as_str() == "\n"
+ && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner1) = args1[1].kind
+ && let ExprKind::Array(elements1) = inner1.kind
+ && elements1.len() == 1
+ && let ExprKind::Call(func2, args2) = elements1[0].kind
+ && let ExprKind::Path(ref qpath2) = func2.kind
+ && args2.len() == 1
+ && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner2) = args2[0].kind
+ && let ExprKind::Path(ref qpath3) = inner2.kind
+ && match_qpath(qpath3, &["x"])
+ && block.expr.is_none()
+ && let PatKind::Binding(BindingAnnotation::NONE, _, name, None) = local.pat.kind
+ && name.as_str() == "print_text"
+{
+ // report your lint here
+}
diff --git a/src/tools/clippy/tests/ui/author/macro_in_loop.rs b/src/tools/clippy/tests/ui/author/macro_in_loop.rs
new file mode 100644
index 000000000..8a520501f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/author/macro_in_loop.rs
@@ -0,0 +1,8 @@
+#![feature(stmt_expr_attributes)]
+
+fn main() {
+ #[clippy::author]
+ for i in 0..1 {
+ println!("{}", i);
+ }
+}
diff --git a/src/tools/clippy/tests/ui/author/macro_in_loop.stdout b/src/tools/clippy/tests/ui/author/macro_in_loop.stdout
new file mode 100644
index 000000000..bd054b6ab
--- /dev/null
+++ b/src/tools/clippy/tests/ui/author/macro_in_loop.stdout
@@ -0,0 +1,48 @@
+if let Some(higher::ForLoop { pat: pat, arg: arg, body: body, .. }) = higher::ForLoop::hir(expr)
+ && let PatKind::Binding(BindingAnnotation::NONE, _, name, None) = pat.kind
+ && name.as_str() == "i"
+ && let ExprKind::Struct(qpath, fields, None) = arg.kind
+ && matches!(qpath, QPath::LangItem(LangItem::Range, _))
+ && fields.len() == 2
+ && fields[0].ident.as_str() == "start"
+ && let ExprKind::Lit(ref lit) = fields[0].expr.kind
+ && let LitKind::Int(0, LitIntType::Unsuffixed) = lit.node
+ && fields[1].ident.as_str() == "end"
+ && let ExprKind::Lit(ref lit1) = fields[1].expr.kind
+ && let LitKind::Int(1, LitIntType::Unsuffixed) = lit1.node
+ && let ExprKind::Block(block, None) = body.kind
+ && block.stmts.len() == 1
+ && let StmtKind::Semi(e) = block.stmts[0].kind
+ && let ExprKind::Block(block1, None) = e.kind
+ && block1.stmts.len() == 1
+ && let StmtKind::Semi(e1) = block1.stmts[0].kind
+ && let ExprKind::Call(func, args) = e1.kind
+ && let ExprKind::Path(ref qpath1) = func.kind
+ && match_qpath(qpath1, &["$crate", "io", "_print"])
+ && args.len() == 1
+ && let ExprKind::Call(func1, args1) = args[0].kind
+ && let ExprKind::Path(ref qpath2) = func1.kind
+ && args1.len() == 2
+ && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner) = args1[0].kind
+ && let ExprKind::Array(elements) = inner.kind
+ && elements.len() == 2
+ && let ExprKind::Lit(ref lit2) = elements[0].kind
+ && let LitKind::Str(s, _) = lit2.node
+ && s.as_str() == ""
+ && let ExprKind::Lit(ref lit3) = elements[1].kind
+ && let LitKind::Str(s1, _) = lit3.node
+ && s1.as_str() == "\n"
+ && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner1) = args1[1].kind
+ && let ExprKind::Array(elements1) = inner1.kind
+ && elements1.len() == 1
+ && let ExprKind::Call(func2, args2) = elements1[0].kind
+ && let ExprKind::Path(ref qpath3) = func2.kind
+ && args2.len() == 1
+ && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner2) = args2[0].kind
+ && let ExprKind::Path(ref qpath4) = inner2.kind
+ && match_qpath(qpath4, &["i"])
+ && block1.expr.is_none()
+ && block.expr.is_none()
+{
+ // report your lint here
+}
diff --git a/src/tools/clippy/tests/ui/auxiliary/proc_macro_derive.rs b/src/tools/clippy/tests/ui/auxiliary/proc_macro_derive.rs
index 37f0ec2b3..79a95d775 100644
--- a/src/tools/clippy/tests/ui/auxiliary/proc_macro_derive.rs
+++ b/src/tools/clippy/tests/ui/auxiliary/proc_macro_derive.rs
@@ -21,6 +21,18 @@ pub fn derive(_: TokenStream) -> TokenStream {
output
}
+#[proc_macro_derive(ImplStructWithStdDisplay)]
+pub fn derive_std(_: TokenStream) -> TokenStream {
+ quote! {
+ struct A {}
+ impl ::std::fmt::Display for A {
+ fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
+ write!(f, "A")
+ }
+ }
+ }
+}
+
#[proc_macro_derive(FieldReassignWithDefault)]
pub fn derive_foo(_input: TokenStream) -> TokenStream {
quote! {
@@ -141,3 +153,19 @@ pub fn shadow_derive(_: TokenStream) -> TokenStream {
.into(),
])
}
+
+#[proc_macro_derive(StructIgnoredUnitPattern)]
+pub fn derive_ignored_unit_pattern(_: TokenStream) -> TokenStream {
+ quote! {
+ struct A;
+ impl A {
+ fn a(&self) -> Result<(), ()> {
+ unimplemented!()
+ }
+
+ pub fn b(&self) {
+ let _ = self.a().unwrap();
+ }
+ }
+ }
+}
diff --git a/src/tools/clippy/tests/ui/auxiliary/proc_macros.rs b/src/tools/clippy/tests/ui/auxiliary/proc_macros.rs
index 43df65438..3303eb145 100644
--- a/src/tools/clippy/tests/ui/auxiliary/proc_macros.rs
+++ b/src/tools/clippy/tests/ui/auxiliary/proc_macros.rs
@@ -379,7 +379,8 @@ impl MacroArm {
p.span(),
)?;
self.add_parenthesized_arg_def(kind, dollar_span, g.span(), out);
- self.args.push(TT::Group(group_with_span(Parenthesis, inner.collect(), g.span())))
+ self.args
+ .push(TT::Group(group_with_span(Parenthesis, inner.collect(), g.span())))
} else {
self.add_multi_arg_def(dollar_span, g.span(), out);
self.args.push(TT::Group(g));
@@ -436,7 +437,12 @@ impl Expander {
&& p.as_char() == ESCAPE_CHAR
&& let Some(arm) = self.arm.as_mut()
{
- arm.add_arg(p.span(), mem::replace(&mut input.tt, tt), &mut input.iter, &mut self.expn)?;
+ arm.add_arg(
+ p.span(),
+ mem::replace(&mut input.tt, tt),
+ &mut input.iter,
+ &mut self.expn,
+ )?;
if input.next().is_none() {
return Ok(());
}
diff --git a/src/tools/clippy/tests/ui/bool_to_int_with_if.fixed b/src/tools/clippy/tests/ui/bool_to_int_with_if.fixed
index 44d7f6e6d..167263d31 100644
--- a/src/tools/clippy/tests/ui/bool_to_int_with_if.fixed
+++ b/src/tools/clippy/tests/ui/bool_to_int_with_if.fixed
@@ -80,9 +80,7 @@ fn main() {
// https://github.com/rust-lang/rust-clippy/issues/10452
let should_not_lint = [(); if true { 1 } else { 0 }];
- let should_not_lint = const {
- if true { 1 } else { 0 }
- };
+ let should_not_lint = const { if true { 1 } else { 0 } };
some_fn(a);
}
@@ -110,7 +108,9 @@ fn if_let(a: Enum, b: Enum) {
0
};
- if let Enum::A = a && let Enum::B = b {
+ if let Enum::A = a
+ && let Enum::B = b
+ {
1
} else {
0
diff --git a/src/tools/clippy/tests/ui/bool_to_int_with_if.rs b/src/tools/clippy/tests/ui/bool_to_int_with_if.rs
index 7d989ae4b..f3f055eb7 100644
--- a/src/tools/clippy/tests/ui/bool_to_int_with_if.rs
+++ b/src/tools/clippy/tests/ui/bool_to_int_with_if.rs
@@ -112,9 +112,7 @@ fn main() {
// https://github.com/rust-lang/rust-clippy/issues/10452
let should_not_lint = [(); if true { 1 } else { 0 }];
- let should_not_lint = const {
- if true { 1 } else { 0 }
- };
+ let should_not_lint = const { if true { 1 } else { 0 } };
some_fn(a);
}
@@ -142,7 +140,9 @@ fn if_let(a: Enum, b: Enum) {
0
};
- if let Enum::A = a && let Enum::B = b {
+ if let Enum::A = a
+ && let Enum::B = b
+ {
1
} else {
0
diff --git a/src/tools/clippy/tests/ui/bool_to_int_with_if.stderr b/src/tools/clippy/tests/ui/bool_to_int_with_if.stderr
index 837ed05d3..714da8a41 100644
--- a/src/tools/clippy/tests/ui/bool_to_int_with_if.stderr
+++ b/src/tools/clippy/tests/ui/bool_to_int_with_if.stderr
@@ -99,7 +99,7 @@ LL | | };
= note: `!b as i32` or `(!b).into()` can also be valid options
error: boolean to int conversion using if
- --> $DIR/bool_to_int_with_if.rs:124:5
+ --> $DIR/bool_to_int_with_if.rs:122:5
|
LL | if a { 1 } else { 0 }
| ^^^^^^^^^^^^^^^^^^^^^ help: replace with from: `u8::from(a)`
diff --git a/src/tools/clippy/tests/ui/builtin_type_shadow.stderr b/src/tools/clippy/tests/ui/builtin_type_shadow.stderr
index cb8462182..e051c00eb 100644
--- a/src/tools/clippy/tests/ui/builtin_type_shadow.stderr
+++ b/src/tools/clippy/tests/ui/builtin_type_shadow.stderr
@@ -13,7 +13,7 @@ error[E0308]: mismatched types
LL | fn foo<u32>(a: u32) -> u32 {
| --- --- expected `u32` because of return type
| |
- | this type parameter
+ | expected this type parameter
LL | 42
| ^^ expected type parameter `u32`, found integer
|
diff --git a/src/tools/clippy/tests/ui/comparison_to_empty.fixed b/src/tools/clippy/tests/ui/comparison_to_empty.fixed
index 90eb50715..e102b13a7 100644
--- a/src/tools/clippy/tests/ui/comparison_to_empty.fixed
+++ b/src/tools/clippy/tests/ui/comparison_to_empty.fixed
@@ -15,7 +15,9 @@ fn main() {
let s = [0].as_slice();
if s.is_empty() {}
if s.is_empty() {}
- if s.is_empty() && s.is_empty() {}
+ if s.is_empty()
+ && s.is_empty()
+ {}
// Allow comparisons to non-empty
let s = String::new();
@@ -28,5 +30,7 @@ fn main() {
if let [0] = &*v {}
let s = [0].as_slice();
if let [0] = s {}
- if let [0] = &*s && s == [0] {}
+ if let [0] = &*s
+ && s == [0]
+ {}
}
diff --git a/src/tools/clippy/tests/ui/comparison_to_empty.rs b/src/tools/clippy/tests/ui/comparison_to_empty.rs
index 0964c4a20..69a6c967d 100644
--- a/src/tools/clippy/tests/ui/comparison_to_empty.rs
+++ b/src/tools/clippy/tests/ui/comparison_to_empty.rs
@@ -15,7 +15,9 @@ fn main() {
let s = [0].as_slice();
if let [] = s {}
if let [] = &*s {}
- if let [] = &*s && s == [] {}
+ if let [] = &*s
+ && s == []
+ {}
// Allow comparisons to non-empty
let s = String::new();
@@ -28,5 +30,7 @@ fn main() {
if let [0] = &*v {}
let s = [0].as_slice();
if let [0] = s {}
- if let [0] = &*s && s == [0] {}
+ if let [0] = &*s
+ && s == [0]
+ {}
}
diff --git a/src/tools/clippy/tests/ui/comparison_to_empty.stderr b/src/tools/clippy/tests/ui/comparison_to_empty.stderr
index b97ab4c3c..83d431fd5 100644
--- a/src/tools/clippy/tests/ui/comparison_to_empty.stderr
+++ b/src/tools/clippy/tests/ui/comparison_to_empty.stderr
@@ -46,14 +46,14 @@ LL | if let [] = &*s {}
error: comparison to empty slice using `if let`
--> $DIR/comparison_to_empty.rs:18:8
|
-LL | if let [] = &*s && s == [] {}
+LL | if let [] = &*s
| ^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `s.is_empty()`
error: comparison to empty slice
- --> $DIR/comparison_to_empty.rs:18:24
+ --> $DIR/comparison_to_empty.rs:19:12
|
-LL | if let [] = &*s && s == [] {}
- | ^^^^^^^ help: using `is_empty` is clearer and more explicit: `s.is_empty()`
+LL | && s == []
+ | ^^^^^^^ help: using `is_empty` is clearer and more explicit: `s.is_empty()`
error: aborting due to 9 previous errors
diff --git a/src/tools/clippy/tests/ui/crashes/ice-10645.stderr b/src/tools/clippy/tests/ui/crashes/ice-10645.stderr
index fc5347c86..7fc62d4fc 100644
--- a/src/tools/clippy/tests/ui/crashes/ice-10645.stderr
+++ b/src/tools/clippy/tests/ui/crashes/ice-10645.stderr
@@ -1,8 +1,8 @@
warning: future cannot be sent between threads safely
- --> $DIR/ice-10645.rs:5:35
+ --> $DIR/ice-10645.rs:5:1
|
LL | pub async fn bar<'a, T: 'a>(_: T) {}
- | ^ future returned by `bar` is not `Send`
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ future returned by `bar` is not `Send`
|
note: captured value is not `Send`
--> $DIR/ice-10645.rs:5:29
diff --git a/src/tools/clippy/tests/ui/crashes/ice-11230.rs b/src/tools/clippy/tests/ui/crashes/ice-11230.rs
new file mode 100644
index 000000000..576188227
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-11230.rs
@@ -0,0 +1,6 @@
+/// Test for https://github.com/rust-lang/rust-clippy/issues/11230
+
+fn main() {
+ const A: &[for<'a> fn(&'a ())] = &[];
+ for v in A.iter() {}
+}
diff --git a/src/tools/clippy/tests/ui/crashes/ice-11755.rs b/src/tools/clippy/tests/ui/crashes/ice-11755.rs
new file mode 100644
index 000000000..367cb6998
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-11755.rs
@@ -0,0 +1,5 @@
+#![warn(clippy::unused_enumerate_index)]
+
+fn main() {
+ for () in [()].iter() {}
+}
diff --git a/src/tools/clippy/tests/ui/crashes/ice-5238.rs b/src/tools/clippy/tests/ui/crashes/ice-5238.rs
index 989eb6d44..b1fc3fb9d 100644
--- a/src/tools/clippy/tests/ui/crashes/ice-5238.rs
+++ b/src/tools/clippy/tests/ui/crashes/ice-5238.rs
@@ -1,6 +1,6 @@
// Regression test for #5238 / https://github.com/rust-lang/rust/pull/69562
-#![feature(generators, generator_trait)]
+#![feature(coroutines, coroutine_trait)]
fn main() {
let _ = || {
diff --git a/src/tools/clippy/tests/ui/crashes/ice-6252.stderr b/src/tools/clippy/tests/ui/crashes/ice-6252.stderr
index cb65360d1..f929bec95 100644
--- a/src/tools/clippy/tests/ui/crashes/ice-6252.stderr
+++ b/src/tools/clippy/tests/ui/crashes/ice-6252.stderr
@@ -31,7 +31,7 @@ LL | const VAL: T;
| ------------ `VAL` from trait
...
LL | impl<N, M> TypeVal<usize> for Multiply<N, M> where N: TypeVal<VAL> {}
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ missing `VAL` in implementation
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ missing `VAL` in implementation
error: aborting due to 3 previous errors
diff --git a/src/tools/clippy/tests/ui/doc/doc-fixable.fixed b/src/tools/clippy/tests/ui/doc/doc-fixable.fixed
index 47b56960a..aee897197 100644
--- a/src/tools/clippy/tests/ui/doc/doc-fixable.fixed
+++ b/src/tools/clippy/tests/ui/doc/doc-fixable.fixed
@@ -224,3 +224,6 @@ where [(); N.checked_next_power_of_two().unwrap()]: {
}
}
}
+
+/// this checks if the lowerCamelCase issue is fixed
+fn issue_11568() {}
diff --git a/src/tools/clippy/tests/ui/doc/doc-fixable.rs b/src/tools/clippy/tests/ui/doc/doc-fixable.rs
index 4d9a4eafa..b6346b881 100644
--- a/src/tools/clippy/tests/ui/doc/doc-fixable.rs
+++ b/src/tools/clippy/tests/ui/doc/doc-fixable.rs
@@ -224,3 +224,6 @@ where [(); N.checked_next_power_of_two().unwrap()]: {
}
}
}
+
+/// this checks if the lowerCamelCase issue is fixed
+fn issue_11568() {}
diff --git a/src/tools/clippy/tests/ui/doc_unsafe.rs b/src/tools/clippy/tests/ui/doc_unsafe.rs
index 0c8eac5cc..f7f41c915 100644
--- a/src/tools/clippy/tests/ui/doc_unsafe.rs
+++ b/src/tools/clippy/tests/ui/doc_unsafe.rs
@@ -1,6 +1,6 @@
//@aux-build:proc_macros.rs
-#![allow(clippy::let_unit_value)]
+#![allow(clippy::let_unit_value, clippy::needless_pass_by_ref_mut)]
extern crate proc_macros;
use proc_macros::external;
diff --git a/src/tools/clippy/tests/ui/enum_glob_use.fixed b/src/tools/clippy/tests/ui/enum_glob_use.fixed
index 9044e8026..3c0db9beb 100644
--- a/src/tools/clippy/tests/ui/enum_glob_use.fixed
+++ b/src/tools/clippy/tests/ui/enum_glob_use.fixed
@@ -19,6 +19,7 @@ mod in_fn_test {
}
mod blurg {
+ #[allow(unused_imports)]
pub use std::cmp::Ordering::*; // ok, re-export
}
diff --git a/src/tools/clippy/tests/ui/enum_glob_use.rs b/src/tools/clippy/tests/ui/enum_glob_use.rs
index 4f157a97c..2538477f7 100644
--- a/src/tools/clippy/tests/ui/enum_glob_use.rs
+++ b/src/tools/clippy/tests/ui/enum_glob_use.rs
@@ -19,6 +19,7 @@ mod in_fn_test {
}
mod blurg {
+ #[allow(unused_imports)]
pub use std::cmp::Ordering::*; // ok, re-export
}
diff --git a/src/tools/clippy/tests/ui/enum_variants.rs b/src/tools/clippy/tests/ui/enum_variants.rs
index 85df852f7..ddf2dfdae 100644
--- a/src/tools/clippy/tests/ui/enum_variants.rs
+++ b/src/tools/clippy/tests/ui/enum_variants.rs
@@ -204,4 +204,21 @@ mod allow_attributes_on_variants {
}
}
+mod issue11494 {
+ // variant order should not affect lint
+ enum Data {
+ Valid,
+ Invalid,
+ DataDependent,
+ //~^ ERROR: variant name starts with the enum's name
+ }
+
+ enum Datas {
+ DatasDependent,
+ //~^ ERROR: variant name starts with the enum's name
+ Valid,
+ Invalid,
+ }
+}
+
fn main() {}
diff --git a/src/tools/clippy/tests/ui/enum_variants.stderr b/src/tools/clippy/tests/ui/enum_variants.stderr
index 9ea80b635..b1e88de0f 100644
--- a/src/tools/clippy/tests/ui/enum_variants.stderr
+++ b/src/tools/clippy/tests/ui/enum_variants.stderr
@@ -158,5 +158,17 @@ LL | | }
|
= help: remove the postfixes and use full paths to the variants instead of glob imports
-error: aborting due to 14 previous errors
+error: variant name starts with the enum's name
+ --> $DIR/enum_variants.rs:212:9
+ |
+LL | DataDependent,
+ | ^^^^^^^^^^^^^
+
+error: variant name starts with the enum's name
+ --> $DIR/enum_variants.rs:217:9
+ |
+LL | DatasDependent,
+ | ^^^^^^^^^^^^^^
+
+error: aborting due to 16 previous errors
diff --git a/src/tools/clippy/tests/ui/floating_point_mul_add.fixed b/src/tools/clippy/tests/ui/floating_point_mul_add.fixed
index c23f4d7c4..a4d6d49e5 100644
--- a/src/tools/clippy/tests/ui/floating_point_mul_add.fixed
+++ b/src/tools/clippy/tests/ui/floating_point_mul_add.fixed
@@ -33,6 +33,9 @@ fn main() {
let _ = a.mul_add(a, b).sqrt();
+ let u = 1usize;
+ let _ = b.mul_add(-(u as f64), a);
+
// Cases where the lint shouldn't be applied
let _ = (a * a + b * b).sqrt();
}
diff --git a/src/tools/clippy/tests/ui/floating_point_mul_add.rs b/src/tools/clippy/tests/ui/floating_point_mul_add.rs
index 431badc8d..262a20f0f 100644
--- a/src/tools/clippy/tests/ui/floating_point_mul_add.rs
+++ b/src/tools/clippy/tests/ui/floating_point_mul_add.rs
@@ -33,6 +33,9 @@ fn main() {
let _ = (a * a + b).sqrt();
+ let u = 1usize;
+ let _ = a - (b * u as f64);
+
// Cases where the lint shouldn't be applied
let _ = (a * a + b * b).sqrt();
}
diff --git a/src/tools/clippy/tests/ui/floating_point_mul_add.stderr b/src/tools/clippy/tests/ui/floating_point_mul_add.stderr
index 81b7126db..38dbefbe1 100644
--- a/src/tools/clippy/tests/ui/floating_point_mul_add.stderr
+++ b/src/tools/clippy/tests/ui/floating_point_mul_add.stderr
@@ -73,5 +73,11 @@ error: multiply and add expressions can be calculated more efficiently and accur
LL | let _ = (a * a + b).sqrt();
| ^^^^^^^^^^^ help: consider using: `a.mul_add(a, b)`
-error: aborting due to 12 previous errors
+error: multiply and add expressions can be calculated more efficiently and accurately
+ --> $DIR/floating_point_mul_add.rs:37:13
+ |
+LL | let _ = a - (b * u as f64);
+ | ^^^^^^^^^^^^^^^^^^ help: consider using: `b.mul_add(-(u as f64), a)`
+
+error: aborting due to 13 previous errors
diff --git a/src/tools/clippy/tests/ui/functions.stderr b/src/tools/clippy/tests/ui/functions.stderr
index 371ea1612..4b06cd038 100644
--- a/src/tools/clippy/tests/ui/functions.stderr
+++ b/src/tools/clippy/tests/ui/functions.stderr
@@ -2,7 +2,7 @@ error: this function has too many arguments (8/7)
--> $DIR/functions.rs:8:1
|
LL | fn bad(_one: u32, _two: u32, _three: &str, _four: bool, _five: f32, _six: f32, _seven: bool, _eight: ()) {}
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `-D clippy::too-many-arguments` implied by `-D warnings`
= help: to override `-D warnings` add `#[allow(clippy::too_many_arguments)]`
@@ -17,7 +17,7 @@ LL | | two: u32,
... |
LL | | eight: ()
LL | | ) {
- | |__^
+ | |_^
error: this function has too many arguments (8/7)
--> $DIR/functions.rs:48:5
@@ -29,7 +29,7 @@ error: this function has too many arguments (8/7)
--> $DIR/functions.rs:58:5
|
LL | fn bad_method(_one: u32, _two: u32, _three: &str, _four: bool, _five: f32, _six: f32, _seven: bool, _eight: ()) {}
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: this public function might dereference a raw pointer but is not marked `unsafe`
--> $DIR/functions.rs:68:34
diff --git a/src/tools/clippy/tests/ui/future_not_send.stderr b/src/tools/clippy/tests/ui/future_not_send.stderr
index f43e3c8ff..7ef4947f1 100644
--- a/src/tools/clippy/tests/ui/future_not_send.stderr
+++ b/src/tools/clippy/tests/ui/future_not_send.stderr
@@ -1,8 +1,8 @@
error: future cannot be sent between threads safely
- --> $DIR/future_not_send.rs:7:62
+ --> $DIR/future_not_send.rs:7:1
|
LL | async fn private_future(rc: Rc<[u8]>, cell: &Cell<usize>) -> bool {
- | ^^^^ future returned by `private_future` is not `Send`
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ future returned by `private_future` is not `Send`
|
note: future is not `Send` as this value is used across an await
--> $DIR/future_not_send.rs:9:20
@@ -23,10 +23,10 @@ LL | async fn private_future(rc: Rc<[u8]>, cell: &Cell<usize>) -> bool {
= help: to override `-D warnings` add `#[allow(clippy::future_not_send)]`
error: future cannot be sent between threads safely
- --> $DIR/future_not_send.rs:12:42
+ --> $DIR/future_not_send.rs:12:1
|
LL | pub async fn public_future(rc: Rc<[u8]>) {
- | ^ future returned by `public_future` is not `Send`
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ future returned by `public_future` is not `Send`
|
note: future is not `Send` as this value is used across an await
--> $DIR/future_not_send.rs:14:20
@@ -39,10 +39,10 @@ LL | async { true }.await;
= note: `std::rc::Rc<[u8]>` doesn't implement `std::marker::Send`
error: future cannot be sent between threads safely
- --> $DIR/future_not_send.rs:21:63
+ --> $DIR/future_not_send.rs:21:1
|
LL | async fn private_future2(rc: Rc<[u8]>, cell: &Cell<usize>) -> bool {
- | ^^^^ future returned by `private_future2` is not `Send`
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ future returned by `private_future2` is not `Send`
|
note: captured value is not `Send`
--> $DIR/future_not_send.rs:21:26
@@ -58,10 +58,10 @@ LL | async fn private_future2(rc: Rc<[u8]>, cell: &Cell<usize>) -> bool {
= note: `std::cell::Cell<usize>` doesn't implement `std::marker::Sync`
error: future cannot be sent between threads safely
- --> $DIR/future_not_send.rs:26:43
+ --> $DIR/future_not_send.rs:26:1
|
LL | pub async fn public_future2(rc: Rc<[u8]>) {}
- | ^ future returned by `public_future2` is not `Send`
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ future returned by `public_future2` is not `Send`
|
note: captured value is not `Send`
--> $DIR/future_not_send.rs:26:29
@@ -71,10 +71,10 @@ LL | pub async fn public_future2(rc: Rc<[u8]>) {}
= note: `std::rc::Rc<[u8]>` doesn't implement `std::marker::Send`
error: future cannot be sent between threads safely
- --> $DIR/future_not_send.rs:38:39
+ --> $DIR/future_not_send.rs:38:5
|
LL | async fn private_future(&self) -> usize {
- | ^^^^^ future returned by `private_future` is not `Send`
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ future returned by `private_future` is not `Send`
|
note: future is not `Send` as this value is used across an await
--> $DIR/future_not_send.rs:40:24
@@ -87,10 +87,10 @@ LL | async { true }.await;
= note: `std::rc::Rc<[u8]>` doesn't implement `std::marker::Sync`
error: future cannot be sent between threads safely
- --> $DIR/future_not_send.rs:44:39
+ --> $DIR/future_not_send.rs:44:5
|
LL | pub async fn public_future(&self) {
- | ^ future returned by `public_future` is not `Send`
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ future returned by `public_future` is not `Send`
|
note: captured value is not `Send` because `&` references cannot be sent unless their referent is `Sync`
--> $DIR/future_not_send.rs:44:32
@@ -100,10 +100,13 @@ LL | pub async fn public_future(&self) {
= note: `std::rc::Rc<[u8]>` doesn't implement `std::marker::Sync`
error: future cannot be sent between threads safely
- --> $DIR/future_not_send.rs:55:37
+ --> $DIR/future_not_send.rs:55:1
|
-LL | async fn generic_future<T>(t: T) -> T
- | ^ future returned by `generic_future` is not `Send`
+LL | / async fn generic_future<T>(t: T) -> T
+LL | |
+LL | | where
+LL | | T: Send,
+ | |____________^ future returned by `generic_future` is not `Send`
|
note: future is not `Send` as this value is used across an await
--> $DIR/future_not_send.rs:61:20
@@ -115,10 +118,10 @@ LL | async { true }.await;
= note: `T` doesn't implement `std::marker::Sync`
error: future cannot be sent between threads safely
- --> $DIR/future_not_send.rs:73:34
+ --> $DIR/future_not_send.rs:73:1
|
LL | async fn unclear_future<T>(t: T) {}
- | ^ future returned by `unclear_future` is not `Send`
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ future returned by `unclear_future` is not `Send`
|
note: captured value is not `Send`
--> $DIR/future_not_send.rs:73:28
diff --git a/src/tools/clippy/tests/ui/get_first.fixed b/src/tools/clippy/tests/ui/get_first.fixed
index b1a597fc4..710ebab1e 100644
--- a/src/tools/clippy/tests/ui/get_first.fixed
+++ b/src/tools/clippy/tests/ui/get_first.fixed
@@ -14,27 +14,37 @@ impl Bar {
fn main() {
let x = vec![2, 3, 5];
- let _ = x.first(); // Use x.first()
+ let _ = x.first();
+ //~^ ERROR: accessing first element with `x.get(0)`
let _ = x.get(1);
let _ = x[0];
let y = [2, 3, 5];
- let _ = y.first(); // Use y.first()
+ let _ = y.first();
+ //~^ ERROR: accessing first element with `y.get(0)`
let _ = y.get(1);
let _ = y[0];
let z = &[2, 3, 5];
- let _ = z.first(); // Use z.first()
+ let _ = z.first();
+ //~^ ERROR: accessing first element with `z.get(0)`
let _ = z.get(1);
let _ = z[0];
let vecdeque: VecDeque<_> = x.iter().cloned().collect();
+ let _ = vecdeque.front();
+ //~^ ERROR: accessing first element with `vecdeque.get(0)`
+ let _ = vecdeque.get(1);
+
let hashmap: HashMap<u8, char> = HashMap::from_iter(vec![(0, 'a'), (1, 'b')]);
let btreemap: BTreeMap<u8, char> = BTreeMap::from_iter(vec![(0, 'a'), (1, 'b')]);
- let _ = vecdeque.get(0); // Do not lint, because VecDeque is not slice.
let _ = hashmap.get(&0); // Do not lint, because HashMap is not slice.
let _ = btreemap.get(&0); // Do not lint, because BTreeMap is not slice.
let bar = Bar { arr: [0, 1, 2] };
let _ = bar.get(0); // Do not lint, because Bar is struct.
+
+ let non_primitives = [vec![1, 2], vec![3, 4]];
+ let _ = non_primitives.first();
+ //~^ ERROR: accessing first element with `non_primitives.get(0)`
}
diff --git a/src/tools/clippy/tests/ui/get_first.rs b/src/tools/clippy/tests/ui/get_first.rs
index e27ee4be8..ad2ba6ce2 100644
--- a/src/tools/clippy/tests/ui/get_first.rs
+++ b/src/tools/clippy/tests/ui/get_first.rs
@@ -14,27 +14,37 @@ impl Bar {
fn main() {
let x = vec![2, 3, 5];
- let _ = x.get(0); // Use x.first()
+ let _ = x.get(0);
+ //~^ ERROR: accessing first element with `x.get(0)`
let _ = x.get(1);
let _ = x[0];
let y = [2, 3, 5];
- let _ = y.get(0); // Use y.first()
+ let _ = y.get(0);
+ //~^ ERROR: accessing first element with `y.get(0)`
let _ = y.get(1);
let _ = y[0];
let z = &[2, 3, 5];
- let _ = z.get(0); // Use z.first()
+ let _ = z.get(0);
+ //~^ ERROR: accessing first element with `z.get(0)`
let _ = z.get(1);
let _ = z[0];
let vecdeque: VecDeque<_> = x.iter().cloned().collect();
+ let _ = vecdeque.get(0);
+ //~^ ERROR: accessing first element with `vecdeque.get(0)`
+ let _ = vecdeque.get(1);
+
let hashmap: HashMap<u8, char> = HashMap::from_iter(vec![(0, 'a'), (1, 'b')]);
let btreemap: BTreeMap<u8, char> = BTreeMap::from_iter(vec![(0, 'a'), (1, 'b')]);
- let _ = vecdeque.get(0); // Do not lint, because VecDeque is not slice.
let _ = hashmap.get(&0); // Do not lint, because HashMap is not slice.
let _ = btreemap.get(&0); // Do not lint, because BTreeMap is not slice.
let bar = Bar { arr: [0, 1, 2] };
let _ = bar.get(0); // Do not lint, because Bar is struct.
+
+ let non_primitives = [vec![1, 2], vec![3, 4]];
+ let _ = non_primitives.get(0);
+ //~^ ERROR: accessing first element with `non_primitives.get(0)`
}
diff --git a/src/tools/clippy/tests/ui/get_first.stderr b/src/tools/clippy/tests/ui/get_first.stderr
index 56b4c29a3..7474a2ada 100644
--- a/src/tools/clippy/tests/ui/get_first.stderr
+++ b/src/tools/clippy/tests/ui/get_first.stderr
@@ -1,23 +1,35 @@
error: accessing first element with `x.get(0)`
--> $DIR/get_first.rs:17:13
|
-LL | let _ = x.get(0); // Use x.first()
+LL | let _ = x.get(0);
| ^^^^^^^^ help: try: `x.first()`
|
= note: `-D clippy::get-first` implied by `-D warnings`
= help: to override `-D warnings` add `#[allow(clippy::get_first)]`
error: accessing first element with `y.get(0)`
- --> $DIR/get_first.rs:22:13
+ --> $DIR/get_first.rs:23:13
|
-LL | let _ = y.get(0); // Use y.first()
+LL | let _ = y.get(0);
| ^^^^^^^^ help: try: `y.first()`
error: accessing first element with `z.get(0)`
- --> $DIR/get_first.rs:27:13
+ --> $DIR/get_first.rs:29:13
|
-LL | let _ = z.get(0); // Use z.first()
+LL | let _ = z.get(0);
| ^^^^^^^^ help: try: `z.first()`
-error: aborting due to 3 previous errors
+error: accessing first element with `vecdeque.get(0)`
+ --> $DIR/get_first.rs:35:13
+ |
+LL | let _ = vecdeque.get(0);
+ | ^^^^^^^^^^^^^^^ help: try: `vecdeque.front()`
+
+error: accessing first element with `non_primitives.get(0)`
+ --> $DIR/get_first.rs:48:13
+ |
+LL | let _ = non_primitives.get(0);
+ | ^^^^^^^^^^^^^^^^^^^^^ help: try: `non_primitives.first()`
+
+error: aborting due to 5 previous errors
diff --git a/src/tools/clippy/tests/ui/if_not_else_bittest.rs b/src/tools/clippy/tests/ui/if_not_else_bittest.rs
new file mode 100644
index 000000000..586ce6ce1
--- /dev/null
+++ b/src/tools/clippy/tests/ui/if_not_else_bittest.rs
@@ -0,0 +1,11 @@
+#![deny(clippy::if_not_else)]
+
+fn show_permissions(flags: u32) {
+ if flags & 0x0F00 != 0 {
+ println!("Has the 0x0F00 permission.");
+ } else {
+ println!("The 0x0F00 permission is missing.");
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/ignored_unit_patterns.fixed b/src/tools/clippy/tests/ui/ignored_unit_patterns.fixed
index 6c6f21fee..707a0e76e 100644
--- a/src/tools/clippy/tests/ui/ignored_unit_patterns.fixed
+++ b/src/tools/clippy/tests/ui/ignored_unit_patterns.fixed
@@ -1,3 +1,4 @@
+//@aux-build:proc_macro_derive.rs
#![warn(clippy::ignored_unit_patterns)]
#![allow(clippy::let_unit_value, clippy::redundant_pattern_matching, clippy::single_match)]
@@ -14,8 +15,22 @@ fn main() {
//~^ ERROR: matching over `()` is more explicit
let _ = foo().map_err(|()| todo!());
//~^ ERROR: matching over `()` is more explicit
+
+ println!(
+ "{:?}",
+ match foo() {
+ Ok(()) => {},
+ //~^ ERROR: matching over `()` is more explicit
+ Err(()) => {},
+ //~^ ERROR: matching over `()` is more explicit
+ }
+ );
}
+// ignored_unit_patterns in derive macro should be ok
+#[derive(proc_macro_derive::StructIgnoredUnitPattern)]
+pub struct B;
+
#[allow(unused)]
pub fn moo(_: ()) {
let () = foo().unwrap();
@@ -23,3 +38,19 @@ pub fn moo(_: ()) {
let _: () = foo().unwrap();
let _: () = ();
}
+
+fn test_unit_ref_1() {
+ let x: (usize, &&&&&()) = (1, &&&&&&());
+ match x {
+ (1, ()) => unimplemented!(),
+ //~^ ERROR: matching over `()` is more explicit
+ _ => unimplemented!(),
+ };
+}
+
+fn test_unit_ref_2(v: &[(usize, ())]) {
+ for (x, ()) in v {
+ //~^ ERROR: matching over `()` is more explicit
+ let _ = x;
+ }
+}
diff --git a/src/tools/clippy/tests/ui/ignored_unit_patterns.rs b/src/tools/clippy/tests/ui/ignored_unit_patterns.rs
index 5e8c2e03b..544f2b8f6 100644
--- a/src/tools/clippy/tests/ui/ignored_unit_patterns.rs
+++ b/src/tools/clippy/tests/ui/ignored_unit_patterns.rs
@@ -1,3 +1,4 @@
+//@aux-build:proc_macro_derive.rs
#![warn(clippy::ignored_unit_patterns)]
#![allow(clippy::let_unit_value, clippy::redundant_pattern_matching, clippy::single_match)]
@@ -14,8 +15,22 @@ fn main() {
//~^ ERROR: matching over `()` is more explicit
let _ = foo().map_err(|_| todo!());
//~^ ERROR: matching over `()` is more explicit
+
+ println!(
+ "{:?}",
+ match foo() {
+ Ok(_) => {},
+ //~^ ERROR: matching over `()` is more explicit
+ Err(_) => {},
+ //~^ ERROR: matching over `()` is more explicit
+ }
+ );
}
+// ignored_unit_patterns in derive macro should be ok
+#[derive(proc_macro_derive::StructIgnoredUnitPattern)]
+pub struct B;
+
#[allow(unused)]
pub fn moo(_: ()) {
let _ = foo().unwrap();
@@ -23,3 +38,19 @@ pub fn moo(_: ()) {
let _: () = foo().unwrap();
let _: () = ();
}
+
+fn test_unit_ref_1() {
+ let x: (usize, &&&&&()) = (1, &&&&&&());
+ match x {
+ (1, _) => unimplemented!(),
+ //~^ ERROR: matching over `()` is more explicit
+ _ => unimplemented!(),
+ };
+}
+
+fn test_unit_ref_2(v: &[(usize, ())]) {
+ for (x, _) in v {
+ //~^ ERROR: matching over `()` is more explicit
+ let _ = x;
+ }
+}
diff --git a/src/tools/clippy/tests/ui/ignored_unit_patterns.stderr b/src/tools/clippy/tests/ui/ignored_unit_patterns.stderr
index df5e1d89e..05c8f281e 100644
--- a/src/tools/clippy/tests/ui/ignored_unit_patterns.stderr
+++ b/src/tools/clippy/tests/ui/ignored_unit_patterns.stderr
@@ -1,5 +1,5 @@
error: matching over `()` is more explicit
- --> $DIR/ignored_unit_patterns.rs:10:12
+ --> $DIR/ignored_unit_patterns.rs:11:12
|
LL | Ok(_) => {},
| ^ help: use `()` instead of `_`: `()`
@@ -8,28 +8,52 @@ LL | Ok(_) => {},
= help: to override `-D warnings` add `#[allow(clippy::ignored_unit_patterns)]`
error: matching over `()` is more explicit
- --> $DIR/ignored_unit_patterns.rs:11:13
+ --> $DIR/ignored_unit_patterns.rs:12:13
|
LL | Err(_) => {},
| ^ help: use `()` instead of `_`: `()`
error: matching over `()` is more explicit
- --> $DIR/ignored_unit_patterns.rs:13:15
+ --> $DIR/ignored_unit_patterns.rs:14:15
|
LL | if let Ok(_) = foo() {}
| ^ help: use `()` instead of `_`: `()`
error: matching over `()` is more explicit
- --> $DIR/ignored_unit_patterns.rs:15:28
+ --> $DIR/ignored_unit_patterns.rs:16:28
|
LL | let _ = foo().map_err(|_| todo!());
| ^ help: use `()` instead of `_`: `()`
error: matching over `()` is more explicit
- --> $DIR/ignored_unit_patterns.rs:21:9
+ --> $DIR/ignored_unit_patterns.rs:22:16
+ |
+LL | Ok(_) => {},
+ | ^ help: use `()` instead of `_`: `()`
+
+error: matching over `()` is more explicit
+ --> $DIR/ignored_unit_patterns.rs:24:17
+ |
+LL | Err(_) => {},
+ | ^ help: use `()` instead of `_`: `()`
+
+error: matching over `()` is more explicit
+ --> $DIR/ignored_unit_patterns.rs:36:9
|
LL | let _ = foo().unwrap();
| ^ help: use `()` instead of `_`: `()`
-error: aborting due to 5 previous errors
+error: matching over `()` is more explicit
+ --> $DIR/ignored_unit_patterns.rs:45:13
+ |
+LL | (1, _) => unimplemented!(),
+ | ^ help: use `()` instead of `_`: `()`
+
+error: matching over `()` is more explicit
+ --> $DIR/ignored_unit_patterns.rs:52:13
+ |
+LL | for (x, _) in v {
+ | ^ help: use `()` instead of `_`: `()`
+
+error: aborting due to 9 previous errors
diff --git a/src/tools/clippy/tests/ui/impl_trait_in_params.rs b/src/tools/clippy/tests/ui/impl_trait_in_params.rs
index b652e4a4a..a6251a370 100644
--- a/src/tools/clippy/tests/ui/impl_trait_in_params.rs
+++ b/src/tools/clippy/tests/ui/impl_trait_in_params.rs
@@ -1,20 +1,47 @@
#![allow(unused)]
#![warn(clippy::impl_trait_in_params)]
+
//@no-rustfix
pub trait Trait {}
pub trait AnotherTrait<T> {}
// Should warn
pub fn a(_: impl Trait) {}
-//~^ ERROR: '`impl Trait` used as a function parameter'
-//~| NOTE: `-D clippy::impl-trait-in-params` implied by `-D warnings`
+//~^ ERROR: `impl Trait` used as a function parameter
pub fn c<C: Trait>(_: C, _: impl Trait) {}
-//~^ ERROR: '`impl Trait` used as a function parameter'
-fn d(_: impl AnotherTrait<u32>) {}
+//~^ ERROR: `impl Trait` used as a function parameter
// Shouldn't warn
pub fn b<B: Trait>(_: B) {}
fn e<T: AnotherTrait<u32>>(_: T) {}
+fn d(_: impl AnotherTrait<u32>) {}
+
+//------ IMPLS
+
+pub trait Public {
+ // See test in ui-toml for a case where avoid-breaking-exported-api is set to false
+ fn t(_: impl Trait);
+ fn tt<T: Trait>(_: T) {}
+}
+
+trait Private {
+ // This shouldn't lint
+ fn t(_: impl Trait);
+ fn tt<T: Trait>(_: T) {}
+}
+
+struct S;
+impl S {
+ pub fn h(_: impl Trait) {} //~ ERROR: `impl Trait` used as a function parameter
+ fn i(_: impl Trait) {}
+ pub fn j<J: Trait>(_: J) {}
+ pub fn k<K: AnotherTrait<u32>>(_: K, _: impl AnotherTrait<u32>) {} //~ ERROR: `impl Trait` used as a function parameter
+}
+
+// Trying with traits
+impl Public for S {
+ fn t(_: impl Trait) {}
+}
fn main() {}
diff --git a/src/tools/clippy/tests/ui/impl_trait_in_params.stderr b/src/tools/clippy/tests/ui/impl_trait_in_params.stderr
index 36b4f27e9..0ae7a3672 100644
--- a/src/tools/clippy/tests/ui/impl_trait_in_params.stderr
+++ b/src/tools/clippy/tests/ui/impl_trait_in_params.stderr
@@ -1,5 +1,5 @@
-error: '`impl Trait` used as a function parameter'
- --> $DIR/impl_trait_in_params.rs:8:13
+error: `impl Trait` used as a function parameter
+ --> $DIR/impl_trait_in_params.rs:9:13
|
LL | pub fn a(_: impl Trait) {}
| ^^^^^^^^^^
@@ -11,7 +11,7 @@ help: add a type parameter
LL | pub fn a<{ /* Generic name */ }: Trait>(_: impl Trait) {}
| +++++++++++++++++++++++++++++++
-error: '`impl Trait` used as a function parameter'
+error: `impl Trait` used as a function parameter
--> $DIR/impl_trait_in_params.rs:11:29
|
LL | pub fn c<C: Trait>(_: C, _: impl Trait) {}
@@ -22,5 +22,27 @@ help: add a type parameter
LL | pub fn c<C: Trait, { /* Generic name */ }: Trait>(_: C, _: impl Trait) {}
| +++++++++++++++++++++++++++++++
-error: aborting due to 2 previous errors
+error: `impl Trait` used as a function parameter
+ --> $DIR/impl_trait_in_params.rs:36:17
+ |
+LL | pub fn h(_: impl Trait) {}
+ | ^^^^^^^^^^
+ |
+help: add a type parameter
+ |
+LL | pub fn h<{ /* Generic name */ }: Trait>(_: impl Trait) {}
+ | +++++++++++++++++++++++++++++++
+
+error: `impl Trait` used as a function parameter
+ --> $DIR/impl_trait_in_params.rs:39:45
+ |
+LL | pub fn k<K: AnotherTrait<u32>>(_: K, _: impl AnotherTrait<u32>) {}
+ | ^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: add a type parameter
+ |
+LL | pub fn k<K: AnotherTrait<u32>, { /* Generic name */ }: AnotherTrait<u32>>(_: K, _: impl AnotherTrait<u32>) {}
+ | +++++++++++++++++++++++++++++++++++++++++++
+
+error: aborting due to 4 previous errors
diff --git a/src/tools/clippy/tests/ui/implied_bounds_in_impls.fixed b/src/tools/clippy/tests/ui/implied_bounds_in_impls.fixed
index a50fa0ccf..fa117aadd 100644
--- a/src/tools/clippy/tests/ui/implied_bounds_in_impls.fixed
+++ b/src/tools/clippy/tests/ui/implied_bounds_in_impls.fixed
@@ -1,6 +1,5 @@
#![warn(clippy::implied_bounds_in_impls)]
#![allow(dead_code)]
-#![feature(return_position_impl_trait_in_trait)]
use std::ops::{Deref, DerefMut};
diff --git a/src/tools/clippy/tests/ui/implied_bounds_in_impls.rs b/src/tools/clippy/tests/ui/implied_bounds_in_impls.rs
index e74ed4425..c96aac151 100644
--- a/src/tools/clippy/tests/ui/implied_bounds_in_impls.rs
+++ b/src/tools/clippy/tests/ui/implied_bounds_in_impls.rs
@@ -1,6 +1,5 @@
#![warn(clippy::implied_bounds_in_impls)]
#![allow(dead_code)]
-#![feature(return_position_impl_trait_in_trait)]
use std::ops::{Deref, DerefMut};
diff --git a/src/tools/clippy/tests/ui/implied_bounds_in_impls.stderr b/src/tools/clippy/tests/ui/implied_bounds_in_impls.stderr
index 72dc2a183..fb44f2aba 100644
--- a/src/tools/clippy/tests/ui/implied_bounds_in_impls.stderr
+++ b/src/tools/clippy/tests/ui/implied_bounds_in_impls.stderr
@@ -1,5 +1,5 @@
error: this bound is already specified as the supertrait of `DerefMut<Target = T>`
- --> $DIR/implied_bounds_in_impls.rs:13:36
+ --> $DIR/implied_bounds_in_impls.rs:12:36
|
LL | fn deref_derefmut<T>(x: T) -> impl Deref<Target = T> + DerefMut<Target = T> {
| ^^^^^^^^^^^^^^^^^
@@ -13,7 +13,7 @@ LL + fn deref_derefmut<T>(x: T) -> impl DerefMut<Target = T> {
|
error: this bound is already specified as the supertrait of `GenericSubtrait<U, W, U>`
- --> $DIR/implied_bounds_in_impls.rs:30:37
+ --> $DIR/implied_bounds_in_impls.rs:29:37
|
LL | fn generics_implied<U, W>() -> impl GenericTrait<W> + GenericSubtrait<U, W, U>
| ^^^^^^^^^^^^^^^
@@ -25,7 +25,7 @@ LL + fn generics_implied<U, W>() -> impl GenericSubtrait<U, W, U>
|
error: this bound is already specified as the supertrait of `GenericSubtrait<(), i32, V>`
- --> $DIR/implied_bounds_in_impls.rs:36:40
+ --> $DIR/implied_bounds_in_impls.rs:35:40
|
LL | fn generics_implied_multi<V>() -> impl GenericTrait<i32> + GenericTrait2<V> + GenericSubtrait<(), i32, V> {}
| ^^^^^^^^^^^^^^^^^
@@ -37,7 +37,7 @@ LL + fn generics_implied_multi<V>() -> impl GenericTrait2<V> + GenericSubtrait<(
|
error: this bound is already specified as the supertrait of `GenericSubtrait<(), i32, V>`
- --> $DIR/implied_bounds_in_impls.rs:36:60
+ --> $DIR/implied_bounds_in_impls.rs:35:60
|
LL | fn generics_implied_multi<V>() -> impl GenericTrait<i32> + GenericTrait2<V> + GenericSubtrait<(), i32, V> {}
| ^^^^^^^^^^^^^^^^
@@ -49,7 +49,7 @@ LL + fn generics_implied_multi<V>() -> impl GenericTrait<i32> + GenericSubtrait<
|
error: this bound is already specified as the supertrait of `GenericSubtrait<(), T, V>`
- --> $DIR/implied_bounds_in_impls.rs:38:44
+ --> $DIR/implied_bounds_in_impls.rs:37:44
|
LL | fn generics_implied_multi2<T, V>() -> impl GenericTrait<T> + GenericTrait2<V> + GenericSubtrait<(), T, V>
| ^^^^^^^^^^^^^^^
@@ -61,7 +61,7 @@ LL + fn generics_implied_multi2<T, V>() -> impl GenericTrait2<V> + GenericSubtra
|
error: this bound is already specified as the supertrait of `GenericSubtrait<(), T, V>`
- --> $DIR/implied_bounds_in_impls.rs:38:62
+ --> $DIR/implied_bounds_in_impls.rs:37:62
|
LL | fn generics_implied_multi2<T, V>() -> impl GenericTrait<T> + GenericTrait2<V> + GenericSubtrait<(), T, V>
| ^^^^^^^^^^^^^^^^
@@ -73,7 +73,7 @@ LL + fn generics_implied_multi2<T, V>() -> impl GenericTrait<T> + GenericSubtrai
|
error: this bound is already specified as the supertrait of `GenericSubtrait<(), i32, ()>`
- --> $DIR/implied_bounds_in_impls.rs:48:28
+ --> $DIR/implied_bounds_in_impls.rs:47:28
|
LL | fn generics_same() -> impl GenericTrait<i32> + GenericSubtrait<(), i32, ()> {}
| ^^^^^^^^^^^^^^^^^
@@ -85,7 +85,7 @@ LL + fn generics_same() -> impl GenericSubtrait<(), i32, ()> {}
|
error: this bound is already specified as the supertrait of `DerefMut<Target = u8>`
- --> $DIR/implied_bounds_in_impls.rs:52:20
+ --> $DIR/implied_bounds_in_impls.rs:51:20
|
LL | fn f() -> impl Deref + DerefMut<Target = u8>;
| ^^^^^
@@ -97,7 +97,7 @@ LL + fn f() -> impl DerefMut<Target = u8>;
|
error: this bound is already specified as the supertrait of `DerefMut<Target = u8>`
- --> $DIR/implied_bounds_in_impls.rs:57:20
+ --> $DIR/implied_bounds_in_impls.rs:56:20
|
LL | fn f() -> impl Deref + DerefMut<Target = u8> {
| ^^^^^
@@ -109,7 +109,7 @@ LL + fn f() -> impl DerefMut<Target = u8> {
|
error: this bound is already specified as the supertrait of `DerefMut<Target = u8>`
- --> $DIR/implied_bounds_in_impls.rs:63:20
+ --> $DIR/implied_bounds_in_impls.rs:62:20
|
LL | fn f() -> impl Deref + DerefMut<Target = u8> {
| ^^^^^
@@ -121,7 +121,7 @@ LL + fn f() -> impl DerefMut<Target = u8> {
|
error: this bound is already specified as the supertrait of `PartialOrd`
- --> $DIR/implied_bounds_in_impls.rs:74:41
+ --> $DIR/implied_bounds_in_impls.rs:73:41
|
LL | fn default_generic_param1() -> impl PartialEq + PartialOrd + Debug {}
| ^^^^^^^^^
@@ -133,7 +133,7 @@ LL + fn default_generic_param1() -> impl PartialOrd + Debug {}
|
error: this bound is already specified as the supertrait of `PartialOrd`
- --> $DIR/implied_bounds_in_impls.rs:75:54
+ --> $DIR/implied_bounds_in_impls.rs:74:54
|
LL | fn default_generic_param2() -> impl PartialOrd + PartialEq + Debug {}
| ^^^^^^^^^
@@ -145,7 +145,7 @@ LL + fn default_generic_param2() -> impl PartialOrd + Debug {}
|
error: this bound is already specified as the supertrait of `DoubleEndedIterator`
- --> $DIR/implied_bounds_in_impls.rs:88:26
+ --> $DIR/implied_bounds_in_impls.rs:87:26
|
LL | fn my_iter() -> impl Iterator<Item = u32> + DoubleEndedIterator {
| ^^^^^^^^^^^^^^^^^^^^
@@ -157,7 +157,7 @@ LL + fn my_iter() -> impl DoubleEndedIterator<Item = u32> {
|
error: this bound is already specified as the supertrait of `Copy`
- --> $DIR/implied_bounds_in_impls.rs:93:27
+ --> $DIR/implied_bounds_in_impls.rs:92:27
|
LL | fn f() -> impl Copy + Clone {
| ^^^^^
@@ -169,7 +169,7 @@ LL + fn f() -> impl Copy {
|
error: this bound is already specified as the supertrait of `Trait2<i32>`
- --> $DIR/implied_bounds_in_impls.rs:107:21
+ --> $DIR/implied_bounds_in_impls.rs:106:21
|
LL | fn f2() -> impl Trait1<i32, U = i64> + Trait2<i32> {}
| ^^^^^^^^^^^^^^^^^^^^
@@ -181,7 +181,7 @@ LL + fn f2() -> impl Trait2<i32, U = i64> {}
|
error: this bound is already specified as the supertrait of `Trait4<i8, X = i32>`
- --> $DIR/implied_bounds_in_impls.rs:122:21
+ --> $DIR/implied_bounds_in_impls.rs:121:21
|
LL | fn f3() -> impl Trait3<i8, i16, i64, X = i32, Y = i128> + Trait4<i8, X = i32> {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/src/tools/clippy/tests/ui/into_iter_without_iter.rs b/src/tools/clippy/tests/ui/into_iter_without_iter.rs
new file mode 100644
index 000000000..448d0114d
--- /dev/null
+++ b/src/tools/clippy/tests/ui/into_iter_without_iter.rs
@@ -0,0 +1,148 @@
+//@no-rustfix
+#![warn(clippy::into_iter_without_iter)]
+
+use std::iter::IntoIterator;
+
+pub struct S1;
+impl<'a> IntoIterator for &'a S1 {
+ //~^ ERROR: `IntoIterator` implemented for a reference type without an `iter` method
+ type IntoIter = std::slice::Iter<'a, u8>;
+ type Item = &'a u8;
+ fn into_iter(self) -> Self::IntoIter {
+ todo!()
+ }
+}
+impl<'a> IntoIterator for &'a mut S1 {
+ //~^ ERROR: `IntoIterator` implemented for a reference type without an `iter_mut` method
+ type IntoIter = std::slice::IterMut<'a, u8>;
+ type Item = &'a mut u8;
+ fn into_iter(self) -> Self::IntoIter {
+ todo!()
+ }
+}
+
+pub struct S2<T>(T);
+impl<'a, T> IntoIterator for &'a S2<T> {
+ //~^ ERROR: `IntoIterator` implemented for a reference type without an `iter` method
+ type IntoIter = std::slice::Iter<'a, T>;
+ type Item = &'a T;
+ fn into_iter(self) -> Self::IntoIter {
+ todo!()
+ }
+}
+impl<'a, T> IntoIterator for &'a mut S2<T> {
+ //~^ ERROR: `IntoIterator` implemented for a reference type without an `iter_mut` method
+ type IntoIter = std::slice::IterMut<'a, T>;
+ type Item = &'a mut T;
+ fn into_iter(self) -> Self::IntoIter {
+ todo!()
+ }
+}
+
+// Both iter and iter_mut methods exist, don't lint
+pub struct S3<'a, T>(&'a T);
+impl<'a, T> S3<'a, T> {
+ fn iter(&self) -> std::slice::Iter<'a, T> {
+ todo!()
+ }
+ fn iter_mut(&mut self) -> std::slice::IterMut<'a, T> {
+ todo!()
+ }
+}
+impl<'a, T> IntoIterator for &S3<'a, T> {
+ type IntoIter = std::slice::Iter<'a, T>;
+ type Item = &'a T;
+ fn into_iter(self) -> Self::IntoIter {
+ todo!()
+ }
+}
+impl<'a, T> IntoIterator for &mut S3<'a, T> {
+ type IntoIter = std::slice::IterMut<'a, T>;
+ type Item = &'a mut T;
+ fn into_iter(self) -> Self::IntoIter {
+ todo!()
+ }
+}
+
+// Only `iter` exists, no `iter_mut`
+pub struct S4<'a, T>(&'a T);
+
+impl<'a, T> S4<'a, T> {
+ fn iter(&self) -> std::slice::Iter<'a, T> {
+ todo!()
+ }
+}
+
+impl<'a, T> IntoIterator for &S4<'a, T> {
+ type IntoIter = std::slice::Iter<'a, T>;
+ type Item = &'a T;
+ fn into_iter(self) -> Self::IntoIter {
+ todo!()
+ }
+}
+
+impl<'a, T> IntoIterator for &mut S4<'a, T> {
+ //~^ ERROR: `IntoIterator` implemented for a reference type without an `iter_mut` method
+ type IntoIter = std::slice::IterMut<'a, T>;
+ type Item = &'a mut T;
+ fn into_iter(self) -> Self::IntoIter {
+ todo!()
+ }
+}
+
+// `iter` exists, but `IntoIterator` is implemented for an alias. inherent_impls doesn't "normalize"
+// aliases so that `inherent_impls(Alias)` where `type Alias = S` returns nothing, so this can lead
+// to fun FPs. Make sure it doesn't happen here (we're using type_of, which should skip the alias).
+pub struct S5;
+
+impl S5 {
+ fn iter(&self) -> std::slice::Iter<'static, u8> {
+ todo!()
+ }
+}
+
+pub type Alias = S5;
+
+impl IntoIterator for &Alias {
+ type IntoIter = std::slice::Iter<'static, u8>;
+ type Item = &'static u8;
+ fn into_iter(self) -> Self::IntoIter {
+ todo!()
+ }
+}
+
+fn main() {}
+
+pub mod issue11635 {
+ // A little more involved than the original repro in the issue, but this tests that it correctly
+ // works for more than one deref step
+
+ use std::ops::Deref;
+
+ pub struct Thing(Vec<u8>);
+ pub struct Thing2(Thing);
+
+ impl Deref for Thing {
+ type Target = [u8];
+
+ fn deref(&self) -> &Self::Target {
+ &self.0
+ }
+ }
+
+ impl Deref for Thing2 {
+ type Target = Thing;
+ fn deref(&self) -> &Self::Target {
+ &self.0
+ }
+ }
+
+ impl<'a> IntoIterator for &'a Thing2 {
+ type Item = &'a u8;
+ type IntoIter = <&'a [u8] as IntoIterator>::IntoIter;
+
+ fn into_iter(self) -> Self::IntoIter {
+ self.0.iter()
+ }
+ }
+}
diff --git a/src/tools/clippy/tests/ui/into_iter_without_iter.stderr b/src/tools/clippy/tests/ui/into_iter_without_iter.stderr
new file mode 100644
index 000000000..70f3f82a9
--- /dev/null
+++ b/src/tools/clippy/tests/ui/into_iter_without_iter.stderr
@@ -0,0 +1,114 @@
+error: `IntoIterator` implemented for a reference type without an `iter` method
+ --> $DIR/into_iter_without_iter.rs:7:1
+ |
+LL | / impl<'a> IntoIterator for &'a S1 {
+LL | |
+LL | | type IntoIter = std::slice::Iter<'a, u8>;
+LL | | type Item = &'a u8;
+... |
+LL | | }
+LL | | }
+ | |_^
+ |
+ = note: `-D clippy::into-iter-without-iter` implied by `-D warnings`
+ = help: to override `-D warnings` add `#[allow(clippy::into_iter_without_iter)]`
+help: consider implementing `iter`
+ |
+LL +
+LL + impl S1 {
+LL + fn iter(&self) -> std::slice::Iter<'a, u8> {
+LL + <&Self as IntoIterator>::into_iter(self)
+LL + }
+LL + }
+ |
+
+error: `IntoIterator` implemented for a reference type without an `iter_mut` method
+ --> $DIR/into_iter_without_iter.rs:15:1
+ |
+LL | / impl<'a> IntoIterator for &'a mut S1 {
+LL | |
+LL | | type IntoIter = std::slice::IterMut<'a, u8>;
+LL | | type Item = &'a mut u8;
+... |
+LL | | }
+LL | | }
+ | |_^
+ |
+help: consider implementing `iter_mut`
+ |
+LL +
+LL + impl S1 {
+LL + fn iter_mut(&mut self) -> std::slice::IterMut<'a, u8> {
+LL + <&mut Self as IntoIterator>::into_iter(self)
+LL + }
+LL + }
+ |
+
+error: `IntoIterator` implemented for a reference type without an `iter` method
+ --> $DIR/into_iter_without_iter.rs:25:1
+ |
+LL | / impl<'a, T> IntoIterator for &'a S2<T> {
+LL | |
+LL | | type IntoIter = std::slice::Iter<'a, T>;
+LL | | type Item = &'a T;
+... |
+LL | | }
+LL | | }
+ | |_^
+ |
+help: consider implementing `iter`
+ |
+LL +
+LL + impl S2<T> {
+LL + fn iter(&self) -> std::slice::Iter<'a, T> {
+LL + <&Self as IntoIterator>::into_iter(self)
+LL + }
+LL + }
+ |
+
+error: `IntoIterator` implemented for a reference type without an `iter_mut` method
+ --> $DIR/into_iter_without_iter.rs:33:1
+ |
+LL | / impl<'a, T> IntoIterator for &'a mut S2<T> {
+LL | |
+LL | | type IntoIter = std::slice::IterMut<'a, T>;
+LL | | type Item = &'a mut T;
+... |
+LL | | }
+LL | | }
+ | |_^
+ |
+help: consider implementing `iter_mut`
+ |
+LL +
+LL + impl S2<T> {
+LL + fn iter_mut(&mut self) -> std::slice::IterMut<'a, T> {
+LL + <&mut Self as IntoIterator>::into_iter(self)
+LL + }
+LL + }
+ |
+
+error: `IntoIterator` implemented for a reference type without an `iter_mut` method
+ --> $DIR/into_iter_without_iter.rs:84:1
+ |
+LL | / impl<'a, T> IntoIterator for &mut S4<'a, T> {
+LL | |
+LL | | type IntoIter = std::slice::IterMut<'a, T>;
+LL | | type Item = &'a mut T;
+... |
+LL | | }
+LL | | }
+ | |_^
+ |
+help: consider implementing `iter_mut`
+ |
+LL +
+LL + impl S4<'a, T> {
+LL + fn iter_mut(&mut self) -> std::slice::IterMut<'a, T> {
+LL + <&mut Self as IntoIterator>::into_iter(self)
+LL + }
+LL + }
+ |
+
+error: aborting due to 5 previous errors
+
diff --git a/src/tools/clippy/tests/ui/items_after_test_module/after_proc_macros.rs b/src/tools/clippy/tests/ui/items_after_test_module/after_proc_macros.rs
new file mode 100644
index 000000000..d9c0aef88
--- /dev/null
+++ b/src/tools/clippy/tests/ui/items_after_test_module/after_proc_macros.rs
@@ -0,0 +1,11 @@
+//@aux-build:../auxiliary/proc_macros.rs
+extern crate proc_macros;
+
+proc_macros::with_span! {
+ span
+ #[cfg(test)]
+ mod tests {}
+}
+
+#[test]
+fn f() {}
diff --git a/src/tools/clippy/tests/ui/items_after_test_module/auxiliary/submodule.rs b/src/tools/clippy/tests/ui/items_after_test_module/auxiliary/submodule.rs
new file mode 100644
index 000000000..69d617901
--- /dev/null
+++ b/src/tools/clippy/tests/ui/items_after_test_module/auxiliary/submodule.rs
@@ -0,0 +1,4 @@
+#[cfg(test)]
+mod tests {}
+
+fn in_submodule() {}
diff --git a/src/tools/clippy/tests/ui/items_after_test_module/block_module.stderr b/src/tools/clippy/tests/ui/items_after_test_module/block_module.stderr
deleted file mode 100644
index 1b6257471..000000000
--- a/src/tools/clippy/tests/ui/items_after_test_module/block_module.stderr
+++ /dev/null
@@ -1,2 +0,0 @@
-error: Option 'test' given more than once
-
diff --git a/src/tools/clippy/tests/ui/items_after_test_module/in_submodule.rs b/src/tools/clippy/tests/ui/items_after_test_module/in_submodule.rs
new file mode 100644
index 000000000..7132e7176
--- /dev/null
+++ b/src/tools/clippy/tests/ui/items_after_test_module/in_submodule.rs
@@ -0,0 +1,8 @@
+#[path = "auxiliary/submodule.rs"]
+mod submodule;
+
+#[cfg(test)]
+mod tests {
+ #[test]
+ fn t() {}
+}
diff --git a/src/tools/clippy/tests/ui/items_after_test_module/in_submodule.stderr b/src/tools/clippy/tests/ui/items_after_test_module/in_submodule.stderr
new file mode 100644
index 000000000..4e9987636
--- /dev/null
+++ b/src/tools/clippy/tests/ui/items_after_test_module/in_submodule.stderr
@@ -0,0 +1,14 @@
+error: items after a test module
+ --> $DIR/auxiliary/submodule.rs:2:1
+ |
+LL | mod tests {}
+ | ^^^^^^^^^
+LL |
+LL | fn in_submodule() {}
+ | ^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::items-after-test-module` implied by `-D warnings`
+ = help: to override `-D warnings` add `#[allow(clippy::items_after_test_module)]`
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui/items_after_test_module/multiple_modules.rs b/src/tools/clippy/tests/ui/items_after_test_module/multiple_modules.rs
new file mode 100644
index 000000000..8ab9e8200
--- /dev/null
+++ b/src/tools/clippy/tests/ui/items_after_test_module/multiple_modules.rs
@@ -0,0 +1,11 @@
+#[cfg(test)]
+mod tests {
+ #[test]
+ fn f() {}
+}
+
+#[cfg(test)]
+mod more_tests {
+ #[test]
+ fn g() {}
+}
diff --git a/src/tools/clippy/tests/ui/items_after_test_module/block_module.rs b/src/tools/clippy/tests/ui/items_after_test_module/root_module.fixed
index 5136b2557..d444100a7 100644
--- a/src/tools/clippy/tests/ui/items_after_test_module/block_module.rs
+++ b/src/tools/clippy/tests/ui/items_after_test_module/root_module.fixed
@@ -1,4 +1,3 @@
-//@compile-flags: --test
#![allow(unused)]
#![warn(clippy::items_after_test_module)]
@@ -6,6 +5,13 @@ fn main() {}
fn should_not_lint() {}
+fn should_lint() {}
+
+const SHOULD_ALSO_LINT: usize = 1;
+macro_rules! should_lint {
+ () => {};
+}
+
#[allow(dead_code)]
#[allow(unused)] // Some attributes to check that span replacement is good enough
#[allow(clippy::allow_attributes)]
@@ -14,10 +20,3 @@ mod tests {
#[test]
fn hi() {}
}
-
-fn should_lint() {}
-
-const SHOULD_ALSO_LINT: usize = 1;
-macro_rules! should_not_lint {
- () => {};
-}
diff --git a/src/tools/clippy/tests/ui/items_after_test_module/root_module.rs b/src/tools/clippy/tests/ui/items_after_test_module/root_module.rs
new file mode 100644
index 000000000..57da01639
--- /dev/null
+++ b/src/tools/clippy/tests/ui/items_after_test_module/root_module.rs
@@ -0,0 +1,22 @@
+#![allow(unused)]
+#![warn(clippy::items_after_test_module)]
+
+fn main() {}
+
+fn should_not_lint() {}
+
+#[allow(dead_code)]
+#[allow(unused)] // Some attributes to check that span replacement is good enough
+#[allow(clippy::allow_attributes)]
+#[cfg(test)]
+mod tests {
+ #[test]
+ fn hi() {}
+}
+
+fn should_lint() {}
+
+const SHOULD_ALSO_LINT: usize = 1;
+macro_rules! should_lint {
+ () => {};
+}
diff --git a/src/tools/clippy/tests/ui/items_after_test_module/root_module.stderr b/src/tools/clippy/tests/ui/items_after_test_module/root_module.stderr
new file mode 100644
index 000000000..67bc82ebf
--- /dev/null
+++ b/src/tools/clippy/tests/ui/items_after_test_module/root_module.stderr
@@ -0,0 +1,20 @@
+error: items after a test module
+ --> $DIR/root_module.rs:12:1
+ |
+LL | mod tests {
+ | ^^^^^^^^^
+...
+LL | fn should_lint() {}
+ | ^^^^^^^^^^^^^^^^
+LL |
+LL | const SHOULD_ALSO_LINT: usize = 1;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | macro_rules! should_lint {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::items-after-test-module` implied by `-D warnings`
+ = help: to override `-D warnings` add `#[allow(clippy::items_after_test_module)]`
+ = help: move the items to before the test module was defined
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui/iter_without_into_iter.rs b/src/tools/clippy/tests/ui/iter_without_into_iter.rs
new file mode 100644
index 000000000..29f526b45
--- /dev/null
+++ b/src/tools/clippy/tests/ui/iter_without_into_iter.rs
@@ -0,0 +1,124 @@
+//@no-rustfix
+#![warn(clippy::iter_without_into_iter)]
+
+pub struct S1;
+impl S1 {
+ pub fn iter(&self) -> std::slice::Iter<'_, u8> {
+ //~^ ERROR: `iter` method without an `IntoIterator` impl
+ [].iter()
+ }
+ pub fn iter_mut(&mut self) -> std::slice::IterMut<'_, u8> {
+ //~^ ERROR: `iter_mut` method without an `IntoIterator` impl
+ [].iter_mut()
+ }
+}
+
+pub struct S2;
+impl S2 {
+ pub fn iter(&self) -> impl Iterator<Item = &u8> {
+ // RPITIT is not stable, so we can't generally suggest it here yet
+ [].iter()
+ }
+}
+
+pub struct S3<'a>(&'a mut [u8]);
+impl<'a> S3<'a> {
+ pub fn iter(&self) -> std::slice::Iter<'_, u8> {
+ //~^ ERROR: `iter` method without an `IntoIterator` impl
+ self.0.iter()
+ }
+ pub fn iter_mut(&mut self) -> std::slice::IterMut<'_, u8> {
+ //~^ ERROR: `iter_mut` method without an `IntoIterator` impl
+ self.0.iter_mut()
+ }
+}
+
+// Incompatible signatures
+pub struct S4;
+impl S4 {
+ pub fn iter(self) -> std::slice::Iter<'static, u8> {
+ todo!()
+ }
+}
+
+pub struct S5;
+impl S5 {
+ pub async fn iter(&self) -> std::slice::Iter<'static, u8> {
+ todo!()
+ }
+}
+
+pub struct S6;
+impl S6 {
+ pub fn iter(&self, _additional_param: ()) -> std::slice::Iter<'static, u8> {
+ todo!()
+ }
+}
+
+pub struct S7<T>(T);
+impl<T> S7<T> {
+ pub fn iter<U>(&self) -> std::slice::Iter<'static, (T, U)> {
+ todo!()
+ }
+}
+
+pub struct S8<T>(T);
+impl<T> S8<T> {
+ pub fn iter(&self) -> std::slice::Iter<'static, T> {
+ todo!()
+ }
+}
+
+// ===========================
+pub struct S9<T>(T);
+impl<T> S9<T> {
+ pub fn iter(&self) -> std::slice::Iter<'_, T> {
+ //~^ ERROR: `iter` method without an `IntoIterator` impl
+ todo!()
+ }
+ pub fn iter_mut(&mut self) -> std::slice::IterMut<'_, T> {
+ //~^ ERROR: `iter_mut` method without an `IntoIterator` impl
+ todo!()
+ }
+}
+
+pub struct S10<T>(T);
+impl<T> S10<T> {
+ pub fn iter(&self) -> std::slice::Iter<'_, T> {
+ // Don't lint, there's an existing (wrong) IntoIterator impl
+ todo!()
+ }
+}
+
+impl<'a, T> IntoIterator for &'a S10<T> {
+ type Item = &'a String;
+ type IntoIter = std::slice::Iter<'a, String>;
+ fn into_iter(self) -> Self::IntoIter {
+ todo!()
+ }
+}
+
+pub struct S11<T>(T);
+impl<T> S11<T> {
+ pub fn iter_mut(&self) -> std::slice::IterMut<'_, T> {
+ // Don't lint, there's an existing (wrong) IntoIterator impl
+ todo!()
+ }
+}
+impl<'a, T> IntoIterator for &'a mut S11<T> {
+ type Item = &'a mut String;
+ type IntoIter = std::slice::IterMut<'a, String>;
+ fn into_iter(self) -> Self::IntoIter {
+ todo!()
+ }
+}
+
+// Private type not exported: don't lint
+struct S12;
+impl S12 {
+ fn iter(&self) -> std::slice::Iter<'_, u8> {
+ todo!()
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/iter_without_into_iter.stderr b/src/tools/clippy/tests/ui/iter_without_into_iter.stderr
new file mode 100644
index 000000000..af5afd47b
--- /dev/null
+++ b/src/tools/clippy/tests/ui/iter_without_into_iter.stderr
@@ -0,0 +1,150 @@
+error: `iter` method without an `IntoIterator` impl for `&S1`
+ --> $DIR/iter_without_into_iter.rs:6:5
+ |
+LL | / pub fn iter(&self) -> std::slice::Iter<'_, u8> {
+LL | |
+LL | | [].iter()
+LL | | }
+ | |_____^
+ |
+ = note: `-D clippy::iter-without-into-iter` implied by `-D warnings`
+ = help: to override `-D warnings` add `#[allow(clippy::iter_without_into_iter)]`
+help: consider implementing `IntoIterator` for `&S1`
+ |
+LL +
+LL + impl IntoIterator for &S1 {
+LL + type IntoIter = std::slice::Iter<'_, u8>;
+LL + type Item = &u8;
+LL + fn into_iter(self) -> Self::IntoIter {
+LL + self.iter()
+LL + }
+LL + }
+ |
+
+error: `iter_mut` method without an `IntoIterator` impl for `&mut S1`
+ --> $DIR/iter_without_into_iter.rs:10:5
+ |
+LL | / pub fn iter_mut(&mut self) -> std::slice::IterMut<'_, u8> {
+LL | |
+LL | | [].iter_mut()
+LL | | }
+ | |_____^
+ |
+help: consider implementing `IntoIterator` for `&mut S1`
+ |
+LL +
+LL + impl IntoIterator for &mut S1 {
+LL + type IntoIter = std::slice::IterMut<'_, u8>;
+LL + type Item = &mut u8;
+LL + fn into_iter(self) -> Self::IntoIter {
+LL + self.iter()
+LL + }
+LL + }
+ |
+
+error: `iter` method without an `IntoIterator` impl for `&S3<'a>`
+ --> $DIR/iter_without_into_iter.rs:26:5
+ |
+LL | / pub fn iter(&self) -> std::slice::Iter<'_, u8> {
+LL | |
+LL | | self.0.iter()
+LL | | }
+ | |_____^
+ |
+help: consider implementing `IntoIterator` for `&S3<'a>`
+ |
+LL +
+LL + impl IntoIterator for &S3<'a> {
+LL + type IntoIter = std::slice::Iter<'_, u8>;
+LL + type Item = &u8;
+LL + fn into_iter(self) -> Self::IntoIter {
+LL + self.iter()
+LL + }
+LL + }
+ |
+
+error: `iter_mut` method without an `IntoIterator` impl for `&mut S3<'a>`
+ --> $DIR/iter_without_into_iter.rs:30:5
+ |
+LL | / pub fn iter_mut(&mut self) -> std::slice::IterMut<'_, u8> {
+LL | |
+LL | | self.0.iter_mut()
+LL | | }
+ | |_____^
+ |
+help: consider implementing `IntoIterator` for `&mut S3<'a>`
+ |
+LL +
+LL + impl IntoIterator for &mut S3<'a> {
+LL + type IntoIter = std::slice::IterMut<'_, u8>;
+LL + type Item = &mut u8;
+LL + fn into_iter(self) -> Self::IntoIter {
+LL + self.iter()
+LL + }
+LL + }
+ |
+
+error: `iter` method without an `IntoIterator` impl for `&S8<T>`
+ --> $DIR/iter_without_into_iter.rs:67:5
+ |
+LL | / pub fn iter(&self) -> std::slice::Iter<'static, T> {
+LL | | todo!()
+LL | | }
+ | |_____^
+ |
+help: consider implementing `IntoIterator` for `&S8<T>`
+ |
+LL +
+LL + impl IntoIterator for &S8<T> {
+LL + type IntoIter = std::slice::Iter<'static, T>;
+LL + type Item = &T;
+LL + fn into_iter(self) -> Self::IntoIter {
+LL + self.iter()
+LL + }
+LL + }
+ |
+
+error: `iter` method without an `IntoIterator` impl for `&S9<T>`
+ --> $DIR/iter_without_into_iter.rs:75:5
+ |
+LL | / pub fn iter(&self) -> std::slice::Iter<'_, T> {
+LL | |
+LL | | todo!()
+LL | | }
+ | |_____^
+ |
+help: consider implementing `IntoIterator` for `&S9<T>`
+ |
+LL +
+LL + impl IntoIterator for &S9<T> {
+LL + type IntoIter = std::slice::Iter<'_, T>;
+LL + type Item = &T;
+LL + fn into_iter(self) -> Self::IntoIter {
+LL + self.iter()
+LL + }
+LL + }
+ |
+
+error: `iter_mut` method without an `IntoIterator` impl for `&mut S9<T>`
+ --> $DIR/iter_without_into_iter.rs:79:5
+ |
+LL | / pub fn iter_mut(&mut self) -> std::slice::IterMut<'_, T> {
+LL | |
+LL | | todo!()
+LL | | }
+ | |_____^
+ |
+help: consider implementing `IntoIterator` for `&mut S9<T>`
+ |
+LL +
+LL + impl IntoIterator for &mut S9<T> {
+LL + type IntoIter = std::slice::IterMut<'_, T>;
+LL + type Item = &mut T;
+LL + fn into_iter(self) -> Self::IntoIter {
+LL + self.iter()
+LL + }
+LL + }
+ |
+
+error: aborting due to 7 previous errors
+
diff --git a/src/tools/clippy/tests/ui/large_futures.fixed b/src/tools/clippy/tests/ui/large_futures.fixed
index 4c192d1c8..aa8c3021b 100644
--- a/src/tools/clippy/tests/ui/large_futures.fixed
+++ b/src/tools/clippy/tests/ui/large_futures.fixed
@@ -1,4 +1,4 @@
-#![feature(generators)]
+#![feature(coroutines)]
#![warn(clippy::large_futures)]
#![allow(clippy::never_loop)]
#![allow(clippy::future_not_send)]
diff --git a/src/tools/clippy/tests/ui/large_futures.rs b/src/tools/clippy/tests/ui/large_futures.rs
index 557d89a9c..fc6ea458d 100644
--- a/src/tools/clippy/tests/ui/large_futures.rs
+++ b/src/tools/clippy/tests/ui/large_futures.rs
@@ -1,4 +1,4 @@
-#![feature(generators)]
+#![feature(coroutines)]
#![warn(clippy::large_futures)]
#![allow(clippy::never_loop)]
#![allow(clippy::future_not_send)]
diff --git a/src/tools/clippy/tests/ui/let_and_return.fixed b/src/tools/clippy/tests/ui/let_and_return.fixed
index 88b8ae673..b5584fcde 100644
--- a/src/tools/clippy/tests/ui/let_and_return.fixed
+++ b/src/tools/clippy/tests/ui/let_and_return.fixed
@@ -168,7 +168,26 @@ mod issue_5729 {
impl<T: Foo + 'static> FooStorage for FooStorageImpl<T> {
fn foo_cloned(&self) -> Arc<dyn Foo> {
- Arc::clone(&self.foo) as _
+ (Arc::clone(&self.foo)) as _
+ //~^ ERROR: returning the result of a `let` binding from a block
+ }
+ }
+}
+
+mod issue_11335 {
+ pub enum E<T> {
+ A(T),
+ B(T),
+ }
+
+ impl<T> E<T> {
+ pub fn inner(&self) -> &T {
+
+
+ (match self {
+ E::A(x) => x,
+ E::B(x) => x,
+ }) as _
//~^ ERROR: returning the result of a `let` binding from a block
}
}
diff --git a/src/tools/clippy/tests/ui/let_and_return.rs b/src/tools/clippy/tests/ui/let_and_return.rs
index f366842c5..f13c7c4e2 100644
--- a/src/tools/clippy/tests/ui/let_and_return.rs
+++ b/src/tools/clippy/tests/ui/let_and_return.rs
@@ -174,6 +174,25 @@ mod issue_5729 {
}
}
+mod issue_11335 {
+ pub enum E<T> {
+ A(T),
+ B(T),
+ }
+
+ impl<T> E<T> {
+ pub fn inner(&self) -> &T {
+ let result = match self {
+ E::A(x) => x,
+ E::B(x) => x,
+ };
+
+ result
+ //~^ ERROR: returning the result of a `let` binding from a block
+ }
+ }
+}
+
// https://github.com/rust-lang/rust-clippy/issues/11167
macro_rules! fn_in_macro {
($b:block) => {
diff --git a/src/tools/clippy/tests/ui/let_and_return.stderr b/src/tools/clippy/tests/ui/let_and_return.stderr
index c09c2b32a..fe60072d1 100644
--- a/src/tools/clippy/tests/ui/let_and_return.stderr
+++ b/src/tools/clippy/tests/ui/let_and_return.stderr
@@ -53,8 +53,30 @@ LL | clone
help: return the expression directly
|
LL ~
-LL ~ Arc::clone(&self.foo) as _
+LL ~ (Arc::clone(&self.foo)) as _
|
-error: aborting due to 4 previous errors
+error: returning the result of a `let` binding from a block
+ --> $DIR/let_and_return.rs:190:13
+ |
+LL | / let result = match self {
+LL | | E::A(x) => x,
+LL | | E::B(x) => x,
+LL | | };
+ | |______________- unnecessary `let` binding
+LL |
+LL | result
+ | ^^^^^^
+ |
+help: return the expression directly
+ |
+LL ~
+LL |
+LL ~ (match self {
+LL + E::A(x) => x,
+LL + E::B(x) => x,
+LL + }) as _
+ |
+
+error: aborting due to 5 previous errors
diff --git a/src/tools/clippy/tests/ui/manual_filter.rs b/src/tools/clippy/tests/ui/manual_filter.rs
index 06968f8ba..ee44909f3 100644
--- a/src/tools/clippy/tests/ui/manual_filter.rs
+++ b/src/tools/clippy/tests/ui/manual_filter.rs
@@ -191,9 +191,7 @@ fn main() {
None => None,
};
let _ = match Some(15) {
- Some(x) => unsafe {
- if f(x) { Some(x) } else { None }
- },
+ Some(x) => unsafe { if f(x) { Some(x) } else { None } },
None => None,
};
diff --git a/src/tools/clippy/tests/ui/manual_filter.stderr b/src/tools/clippy/tests/ui/manual_filter.stderr
index 1490f2097..b23ad887e 100644
--- a/src/tools/clippy/tests/ui/manual_filter.stderr
+++ b/src/tools/clippy/tests/ui/manual_filter.stderr
@@ -169,15 +169,13 @@ error: manual implementation of `Option::filter`
|
LL | let _ = match Some(15) {
| _____________^
-LL | | Some(x) => unsafe {
-LL | | if f(x) { Some(x) } else { None }
-LL | | },
+LL | | Some(x) => unsafe { if f(x) { Some(x) } else { None } },
LL | | None => None,
LL | | };
| |_____^ help: try: `Some(15).filter(|&x| unsafe { f(x) })`
error: manual implementation of `Option::filter`
- --> $DIR/manual_filter.rs:203:12
+ --> $DIR/manual_filter.rs:201:12
|
LL | } else if let Some(x) = Some(16) {
| ____________^
diff --git a/src/tools/clippy/tests/ui/manual_filter_map.fixed b/src/tools/clippy/tests/ui/manual_filter_map.fixed
index 4de45e39b..a44c46c14 100644
--- a/src/tools/clippy/tests/ui/manual_filter_map.fixed
+++ b/src/tools/clippy/tests/ui/manual_filter_map.fixed
@@ -2,6 +2,7 @@
#![warn(clippy::manual_filter_map)]
#![allow(clippy::redundant_closure)] // FIXME suggestion may have redundant closure
#![allow(clippy::useless_vec)]
+#![allow(clippy::struct_field_names)]
fn main() {
// is_some(), unwrap()
diff --git a/src/tools/clippy/tests/ui/manual_filter_map.rs b/src/tools/clippy/tests/ui/manual_filter_map.rs
index 22f316f90..e72d0c430 100644
--- a/src/tools/clippy/tests/ui/manual_filter_map.rs
+++ b/src/tools/clippy/tests/ui/manual_filter_map.rs
@@ -2,6 +2,7 @@
#![warn(clippy::manual_filter_map)]
#![allow(clippy::redundant_closure)] // FIXME suggestion may have redundant closure
#![allow(clippy::useless_vec)]
+#![allow(clippy::struct_field_names)]
fn main() {
// is_some(), unwrap()
diff --git a/src/tools/clippy/tests/ui/manual_filter_map.stderr b/src/tools/clippy/tests/ui/manual_filter_map.stderr
index 0bfc1f5c7..cf64bb259 100644
--- a/src/tools/clippy/tests/ui/manual_filter_map.stderr
+++ b/src/tools/clippy/tests/ui/manual_filter_map.stderr
@@ -1,11 +1,11 @@
error: `filter(..).map(..)` can be simplified as `filter_map(..)`
- --> $DIR/manual_filter_map.rs:8:19
+ --> $DIR/manual_filter_map.rs:9:19
|
LL | let _ = (0..).filter(|n| to_opt(*n).is_some()).map(|a| to_opt(a).unwrap());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `filter_map(|a| to_opt(a))`
|
note: the suggestion might change the behavior of the program when merging `filter` and `map`, because this expression potentially contains side effects and will only execute once
- --> $DIR/manual_filter_map.rs:8:30
+ --> $DIR/manual_filter_map.rs:9:30
|
LL | let _ = (0..).filter(|n| to_opt(*n).is_some()).map(|a| to_opt(a).unwrap());
| ^^^^^^^^^^
@@ -13,31 +13,31 @@ LL | let _ = (0..).filter(|n| to_opt(*n).is_some()).map(|a| to_opt(a).unwrap
= help: to override `-D warnings` add `#[allow(clippy::manual_filter_map)]`
error: `filter(..).map(..)` can be simplified as `filter_map(..)`
- --> $DIR/manual_filter_map.rs:11:19
+ --> $DIR/manual_filter_map.rs:12:19
|
LL | let _ = (0..).filter(|&n| to_opt(n).is_some()).map(|a| to_opt(a).expect("hi"));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `filter_map(|a| to_opt(a))`
|
note: the suggestion might change the behavior of the program when merging `filter` and `map`, because this expression potentially contains side effects and will only execute once
- --> $DIR/manual_filter_map.rs:11:31
+ --> $DIR/manual_filter_map.rs:12:31
|
LL | let _ = (0..).filter(|&n| to_opt(n).is_some()).map(|a| to_opt(a).expect("hi"));
| ^^^^^^^^^
error: `filter(..).map(..)` can be simplified as `filter_map(..)`
- --> $DIR/manual_filter_map.rs:14:19
+ --> $DIR/manual_filter_map.rs:15:19
|
LL | let _ = (0..).filter(|&n| to_res(n).is_ok()).map(|a| to_res(a).unwrap_or(1));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `filter_map(|a| to_res(a).ok())`
|
note: the suggestion might change the behavior of the program when merging `filter` and `map`, because this expression potentially contains side effects and will only execute once
- --> $DIR/manual_filter_map.rs:14:31
+ --> $DIR/manual_filter_map.rs:15:31
|
LL | let _ = (0..).filter(|&n| to_res(n).is_ok()).map(|a| to_res(a).unwrap_or(1));
| ^^^^^^^^^
error: `filter(..).map(..)` can be simplified as `filter_map(..)`
- --> $DIR/manual_filter_map.rs:17:10
+ --> $DIR/manual_filter_map.rs:18:10
|
LL | .filter(|&x| to_ref(to_opt(x)).is_some())
| __________^
@@ -45,13 +45,13 @@ LL | | .map(|y| to_ref(to_opt(y)).unwrap());
| |____________________________________________^ help: try: `filter_map(|y| *to_ref(to_opt(y)))`
|
note: the suggestion might change the behavior of the program when merging `filter` and `map`, because this expression potentially contains side effects and will only execute once
- --> $DIR/manual_filter_map.rs:17:22
+ --> $DIR/manual_filter_map.rs:18:22
|
LL | .filter(|&x| to_ref(to_opt(x)).is_some())
| ^^^^^^^^^^^^^^^^^
error: `filter(..).map(..)` can be simplified as `filter_map(..)`
- --> $DIR/manual_filter_map.rs:20:10
+ --> $DIR/manual_filter_map.rs:21:10
|
LL | .filter(|x| to_ref(to_opt(*x)).is_some())
| __________^
@@ -59,13 +59,13 @@ LL | | .map(|y| to_ref(to_opt(y)).unwrap());
| |____________________________________________^ help: try: `filter_map(|y| *to_ref(to_opt(y)))`
|
note: the suggestion might change the behavior of the program when merging `filter` and `map`, because this expression potentially contains side effects and will only execute once
- --> $DIR/manual_filter_map.rs:20:21
+ --> $DIR/manual_filter_map.rs:21:21
|
LL | .filter(|x| to_ref(to_opt(*x)).is_some())
| ^^^^^^^^^^^^^^^^^^
error: `filter(..).map(..)` can be simplified as `filter_map(..)`
- --> $DIR/manual_filter_map.rs:24:10
+ --> $DIR/manual_filter_map.rs:25:10
|
LL | .filter(|&x| to_ref(to_res(x)).is_ok())
| __________^
@@ -73,13 +73,13 @@ LL | | .map(|y| to_ref(to_res(y)).unwrap());
| |____________________________________________^ help: try: `filter_map(|y| to_ref(to_res(y)).ok())`
|
note: the suggestion might change the behavior of the program when merging `filter` and `map`, because this expression potentially contains side effects and will only execute once
- --> $DIR/manual_filter_map.rs:24:22
+ --> $DIR/manual_filter_map.rs:25:22
|
LL | .filter(|&x| to_ref(to_res(x)).is_ok())
| ^^^^^^^^^^^^^^^^^
error: `filter(..).map(..)` can be simplified as `filter_map(..)`
- --> $DIR/manual_filter_map.rs:27:10
+ --> $DIR/manual_filter_map.rs:28:10
|
LL | .filter(|x| to_ref(to_res(*x)).is_ok())
| __________^
@@ -87,13 +87,13 @@ LL | | .map(|y| to_ref(to_res(y)).unwrap());
| |____________________________________________^ help: try: `filter_map(|y| to_ref(to_res(y)).ok())`
|
note: the suggestion might change the behavior of the program when merging `filter` and `map`, because this expression potentially contains side effects and will only execute once
- --> $DIR/manual_filter_map.rs:27:21
+ --> $DIR/manual_filter_map.rs:28:21
|
LL | .filter(|x| to_ref(to_res(*x)).is_ok())
| ^^^^^^^^^^^^^^^^^^
error: `find(..).map(..)` can be simplified as `find_map(..)`
- --> $DIR/manual_filter_map.rs:33:27
+ --> $DIR/manual_filter_map.rs:34:27
|
LL | iter::<Option<&u8>>().find(|x| x.is_some()).map(|x| x.cloned().unwrap());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x.cloned())`
@@ -102,79 +102,79 @@ LL | iter::<Option<&u8>>().find(|x| x.is_some()).map(|x| x.cloned().unwrap()
= help: to override `-D warnings` add `#[allow(clippy::manual_find_map)]`
error: `find(..).map(..)` can be simplified as `find_map(..)`
- --> $DIR/manual_filter_map.rs:34:28
+ --> $DIR/manual_filter_map.rs:35:28
|
LL | iter::<&Option<&u8>>().find(|x| x.is_some()).map(|x| x.cloned().unwrap());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x.cloned())`
error: `find(..).map(..)` can be simplified as `find_map(..)`
- --> $DIR/manual_filter_map.rs:35:31
+ --> $DIR/manual_filter_map.rs:36:31
|
LL | iter::<&Option<String>>().find(|x| x.is_some()).map(|x| x.as_deref().unwrap());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x.as_deref())`
error: `find(..).map(..)` can be simplified as `find_map(..)`
- --> $DIR/manual_filter_map.rs:36:31
+ --> $DIR/manual_filter_map.rs:37:31
|
LL | iter::<Option<&String>>().find(|&x| to_ref(x).is_some()).map(|y| to_ref(y).cloned().unwrap());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|y| to_ref(y).cloned())`
|
note: the suggestion might change the behavior of the program when merging `filter` and `map`, because this expression potentially contains side effects and will only execute once
- --> $DIR/manual_filter_map.rs:36:41
+ --> $DIR/manual_filter_map.rs:37:41
|
LL | iter::<Option<&String>>().find(|&x| to_ref(x).is_some()).map(|y| to_ref(y).cloned().unwrap());
| ^^^^^^^^^
error: `find(..).map(..)` can be simplified as `find_map(..)`
- --> $DIR/manual_filter_map.rs:38:30
+ --> $DIR/manual_filter_map.rs:39:30
|
LL | iter::<Result<u8, ()>>().find(|x| x.is_ok()).map(|x| x.unwrap());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x.ok())`
error: `find(..).map(..)` can be simplified as `find_map(..)`
- --> $DIR/manual_filter_map.rs:39:31
+ --> $DIR/manual_filter_map.rs:40:31
|
LL | iter::<&Result<u8, ()>>().find(|x| x.is_ok()).map(|x| x.unwrap());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x.ok())`
error: `find(..).map(..)` can be simplified as `find_map(..)`
- --> $DIR/manual_filter_map.rs:40:32
+ --> $DIR/manual_filter_map.rs:41:32
|
LL | iter::<&&Result<u8, ()>>().find(|x| x.is_ok()).map(|x| x.unwrap());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x.ok())`
error: `find(..).map(..)` can be simplified as `find_map(..)`
- --> $DIR/manual_filter_map.rs:41:31
+ --> $DIR/manual_filter_map.rs:42:31
|
LL | iter::<Result<&u8, ()>>().find(|x| x.is_ok()).map(|x| x.cloned().unwrap());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x.cloned().ok())`
error: `find(..).map(..)` can be simplified as `find_map(..)`
- --> $DIR/manual_filter_map.rs:42:32
+ --> $DIR/manual_filter_map.rs:43:32
|
LL | iter::<&Result<&u8, ()>>().find(|x| x.is_ok()).map(|x| x.cloned().unwrap());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x.cloned().ok())`
error: `find(..).map(..)` can be simplified as `find_map(..)`
- --> $DIR/manual_filter_map.rs:43:35
+ --> $DIR/manual_filter_map.rs:44:35
|
LL | iter::<&Result<String, ()>>().find(|x| x.is_ok()).map(|x| x.as_deref().unwrap());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x.as_deref().ok())`
error: `find(..).map(..)` can be simplified as `find_map(..)`
- --> $DIR/manual_filter_map.rs:44:35
+ --> $DIR/manual_filter_map.rs:45:35
|
LL | iter::<Result<&String, ()>>().find(|&x| to_ref(x).is_ok()).map(|y| to_ref(y).cloned().unwrap());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|y| to_ref(y).cloned().ok())`
|
note: the suggestion might change the behavior of the program when merging `filter` and `map`, because this expression potentially contains side effects and will only execute once
- --> $DIR/manual_filter_map.rs:44:45
+ --> $DIR/manual_filter_map.rs:45:45
|
LL | iter::<Result<&String, ()>>().find(|&x| to_ref(x).is_ok()).map(|y| to_ref(y).cloned().unwrap());
| ^^^^^^^^^
error: `filter(..).map(..)` can be simplified as `filter_map(..)`
- --> $DIR/manual_filter_map.rs:92:10
+ --> $DIR/manual_filter_map.rs:93:10
|
LL | .filter(|f| f.option_field.is_some())
| __________^
@@ -182,7 +182,7 @@ LL | | .map(|f| f.option_field.clone().unwrap());
| |_________________________________________________^ help: try: `filter_map(|f| f.option_field.clone())`
error: `filter(..).map(..)` can be simplified as `filter_map(..)`
- --> $DIR/manual_filter_map.rs:97:10
+ --> $DIR/manual_filter_map.rs:98:10
|
LL | .filter(|f| f.ref_field.is_some())
| __________^
@@ -190,7 +190,7 @@ LL | | .map(|f| f.ref_field.cloned().unwrap());
| |_______________________________________________^ help: try: `filter_map(|f| f.ref_field.cloned())`
error: `filter(..).map(..)` can be simplified as `filter_map(..)`
- --> $DIR/manual_filter_map.rs:102:10
+ --> $DIR/manual_filter_map.rs:103:10
|
LL | .filter(|f| f.ref_field.is_some())
| __________^
@@ -198,7 +198,7 @@ LL | | .map(|f| f.ref_field.copied().unwrap());
| |_______________________________________________^ help: try: `filter_map(|f| f.ref_field.copied())`
error: `filter(..).map(..)` can be simplified as `filter_map(..)`
- --> $DIR/manual_filter_map.rs:107:10
+ --> $DIR/manual_filter_map.rs:108:10
|
LL | .filter(|f| f.result_field.is_ok())
| __________^
@@ -206,7 +206,7 @@ LL | | .map(|f| f.result_field.clone().unwrap());
| |_________________________________________________^ help: try: `filter_map(|f| f.result_field.clone().ok())`
error: `filter(..).map(..)` can be simplified as `filter_map(..)`
- --> $DIR/manual_filter_map.rs:112:10
+ --> $DIR/manual_filter_map.rs:113:10
|
LL | .filter(|f| f.result_field.is_ok())
| __________^
@@ -214,7 +214,7 @@ LL | | .map(|f| f.result_field.as_ref().unwrap());
| |__________________________________________________^ help: try: `filter_map(|f| f.result_field.as_ref().ok())`
error: `filter(..).map(..)` can be simplified as `filter_map(..)`
- --> $DIR/manual_filter_map.rs:117:10
+ --> $DIR/manual_filter_map.rs:118:10
|
LL | .filter(|f| f.result_field.is_ok())
| __________^
@@ -222,7 +222,7 @@ LL | | .map(|f| f.result_field.as_deref().unwrap());
| |____________________________________________________^ help: try: `filter_map(|f| f.result_field.as_deref().ok())`
error: `filter(..).map(..)` can be simplified as `filter_map(..)`
- --> $DIR/manual_filter_map.rs:122:10
+ --> $DIR/manual_filter_map.rs:123:10
|
LL | .filter(|f| f.result_field.is_ok())
| __________^
@@ -230,7 +230,7 @@ LL | | .map(|f| f.result_field.as_mut().unwrap());
| |__________________________________________________^ help: try: `filter_map(|f| f.result_field.as_mut().ok())`
error: `filter(..).map(..)` can be simplified as `filter_map(..)`
- --> $DIR/manual_filter_map.rs:127:10
+ --> $DIR/manual_filter_map.rs:128:10
|
LL | .filter(|f| f.result_field.is_ok())
| __________^
@@ -238,7 +238,7 @@ LL | | .map(|f| f.result_field.as_deref_mut().unwrap());
| |________________________________________________________^ help: try: `filter_map(|f| f.result_field.as_deref_mut().ok())`
error: `filter(..).map(..)` can be simplified as `filter_map(..)`
- --> $DIR/manual_filter_map.rs:132:10
+ --> $DIR/manual_filter_map.rs:133:10
|
LL | .filter(|f| f.result_field.is_ok())
| __________^
@@ -246,7 +246,7 @@ LL | | .map(|f| f.result_field.to_owned().unwrap());
| |____________________________________________________^ help: try: `filter_map(|f| f.result_field.to_owned().ok())`
error: `filter(..).map(..)` can be simplified as `filter_map(..)`
- --> $DIR/manual_filter_map.rs:145:27
+ --> $DIR/manual_filter_map.rs:146:27
|
LL | let _x = iter.clone().filter(|x| matches!(x, Enum::A(_))).map(|x| match x {
| ___________________________^
@@ -256,7 +256,7 @@ LL | | });
| |______^ help: try: `filter_map(|x| match x { Enum::A(s) => Some(s), _ => None })`
error: `filter(..).map(..)` can be simplified as `filter_map(..)`
- --> $DIR/manual_filter_map.rs:155:10
+ --> $DIR/manual_filter_map.rs:156:10
|
LL | .filter(|x| matches!(x, Enum::A(_)))
| __________^
diff --git a/src/tools/clippy/tests/ui/manual_find_map.fixed b/src/tools/clippy/tests/ui/manual_find_map.fixed
index 0e92d25e6..2d9a356b9 100644
--- a/src/tools/clippy/tests/ui/manual_find_map.fixed
+++ b/src/tools/clippy/tests/ui/manual_find_map.fixed
@@ -2,6 +2,7 @@
#![warn(clippy::manual_find_map)]
#![allow(clippy::redundant_closure)] // FIXME suggestion may have redundant closure
#![allow(clippy::useless_vec)]
+#![allow(clippy::struct_field_names)]
fn main() {
// is_some(), unwrap()
diff --git a/src/tools/clippy/tests/ui/manual_find_map.rs b/src/tools/clippy/tests/ui/manual_find_map.rs
index b2568c823..7c5cc1366 100644
--- a/src/tools/clippy/tests/ui/manual_find_map.rs
+++ b/src/tools/clippy/tests/ui/manual_find_map.rs
@@ -2,6 +2,7 @@
#![warn(clippy::manual_find_map)]
#![allow(clippy::redundant_closure)] // FIXME suggestion may have redundant closure
#![allow(clippy::useless_vec)]
+#![allow(clippy::struct_field_names)]
fn main() {
// is_some(), unwrap()
diff --git a/src/tools/clippy/tests/ui/manual_find_map.stderr b/src/tools/clippy/tests/ui/manual_find_map.stderr
index 0dc9ae1df..052638232 100644
--- a/src/tools/clippy/tests/ui/manual_find_map.stderr
+++ b/src/tools/clippy/tests/ui/manual_find_map.stderr
@@ -1,11 +1,11 @@
error: `find(..).map(..)` can be simplified as `find_map(..)`
- --> $DIR/manual_find_map.rs:8:19
+ --> $DIR/manual_find_map.rs:9:19
|
LL | let _ = (0..).find(|n| to_opt(*n).is_some()).map(|a| to_opt(a).unwrap());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|a| to_opt(a))`
|
note: the suggestion might change the behavior of the program when merging `filter` and `map`, because this expression potentially contains side effects and will only execute once
- --> $DIR/manual_find_map.rs:8:28
+ --> $DIR/manual_find_map.rs:9:28
|
LL | let _ = (0..).find(|n| to_opt(*n).is_some()).map(|a| to_opt(a).unwrap());
| ^^^^^^^^^^
@@ -13,31 +13,31 @@ LL | let _ = (0..).find(|n| to_opt(*n).is_some()).map(|a| to_opt(a).unwrap()
= help: to override `-D warnings` add `#[allow(clippy::manual_find_map)]`
error: `find(..).map(..)` can be simplified as `find_map(..)`
- --> $DIR/manual_find_map.rs:11:19
+ --> $DIR/manual_find_map.rs:12:19
|
LL | let _ = (0..).find(|&n| to_opt(n).is_some()).map(|a| to_opt(a).expect("hi"));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|a| to_opt(a))`
|
note: the suggestion might change the behavior of the program when merging `filter` and `map`, because this expression potentially contains side effects and will only execute once
- --> $DIR/manual_find_map.rs:11:29
+ --> $DIR/manual_find_map.rs:12:29
|
LL | let _ = (0..).find(|&n| to_opt(n).is_some()).map(|a| to_opt(a).expect("hi"));
| ^^^^^^^^^
error: `find(..).map(..)` can be simplified as `find_map(..)`
- --> $DIR/manual_find_map.rs:14:19
+ --> $DIR/manual_find_map.rs:15:19
|
LL | let _ = (0..).find(|&n| to_res(n).is_ok()).map(|a| to_res(a).unwrap_or(1));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|a| to_res(a).ok())`
|
note: the suggestion might change the behavior of the program when merging `filter` and `map`, because this expression potentially contains side effects and will only execute once
- --> $DIR/manual_find_map.rs:14:29
+ --> $DIR/manual_find_map.rs:15:29
|
LL | let _ = (0..).find(|&n| to_res(n).is_ok()).map(|a| to_res(a).unwrap_or(1));
| ^^^^^^^^^
error: `find(..).map(..)` can be simplified as `find_map(..)`
- --> $DIR/manual_find_map.rs:17:10
+ --> $DIR/manual_find_map.rs:18:10
|
LL | .find(|&x| to_ref(to_opt(x)).is_some())
| __________^
@@ -45,13 +45,13 @@ LL | | .map(|y| to_ref(to_opt(y)).unwrap());
| |____________________________________________^ help: try: `find_map(|y| *to_ref(to_opt(y)))`
|
note: the suggestion might change the behavior of the program when merging `filter` and `map`, because this expression potentially contains side effects and will only execute once
- --> $DIR/manual_find_map.rs:17:20
+ --> $DIR/manual_find_map.rs:18:20
|
LL | .find(|&x| to_ref(to_opt(x)).is_some())
| ^^^^^^^^^^^^^^^^^
error: `find(..).map(..)` can be simplified as `find_map(..)`
- --> $DIR/manual_find_map.rs:20:10
+ --> $DIR/manual_find_map.rs:21:10
|
LL | .find(|x| to_ref(to_opt(*x)).is_some())
| __________^
@@ -59,13 +59,13 @@ LL | | .map(|y| to_ref(to_opt(y)).unwrap());
| |____________________________________________^ help: try: `find_map(|y| *to_ref(to_opt(y)))`
|
note: the suggestion might change the behavior of the program when merging `filter` and `map`, because this expression potentially contains side effects and will only execute once
- --> $DIR/manual_find_map.rs:20:19
+ --> $DIR/manual_find_map.rs:21:19
|
LL | .find(|x| to_ref(to_opt(*x)).is_some())
| ^^^^^^^^^^^^^^^^^^
error: `find(..).map(..)` can be simplified as `find_map(..)`
- --> $DIR/manual_find_map.rs:24:10
+ --> $DIR/manual_find_map.rs:25:10
|
LL | .find(|&x| to_ref(to_res(x)).is_ok())
| __________^
@@ -73,13 +73,13 @@ LL | | .map(|y| to_ref(to_res(y)).unwrap());
| |____________________________________________^ help: try: `find_map(|y| to_ref(to_res(y)).ok())`
|
note: the suggestion might change the behavior of the program when merging `filter` and `map`, because this expression potentially contains side effects and will only execute once
- --> $DIR/manual_find_map.rs:24:20
+ --> $DIR/manual_find_map.rs:25:20
|
LL | .find(|&x| to_ref(to_res(x)).is_ok())
| ^^^^^^^^^^^^^^^^^
error: `find(..).map(..)` can be simplified as `find_map(..)`
- --> $DIR/manual_find_map.rs:27:10
+ --> $DIR/manual_find_map.rs:28:10
|
LL | .find(|x| to_ref(to_res(*x)).is_ok())
| __________^
@@ -87,109 +87,109 @@ LL | | .map(|y| to_ref(to_res(y)).unwrap());
| |____________________________________________^ help: try: `find_map(|y| to_ref(to_res(y)).ok())`
|
note: the suggestion might change the behavior of the program when merging `filter` and `map`, because this expression potentially contains side effects and will only execute once
- --> $DIR/manual_find_map.rs:27:19
+ --> $DIR/manual_find_map.rs:28:19
|
LL | .find(|x| to_ref(to_res(*x)).is_ok())
| ^^^^^^^^^^^^^^^^^^
error: `find(..).map(..)` can be simplified as `find_map(..)`
- --> $DIR/manual_find_map.rs:33:26
+ --> $DIR/manual_find_map.rs:34:26
|
LL | iter::<Option<u8>>().find(|x| x.is_some()).map(|x| x.unwrap());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x)`
error: `find(..).map(..)` can be simplified as `find_map(..)`
- --> $DIR/manual_find_map.rs:34:27
+ --> $DIR/manual_find_map.rs:35:27
|
LL | iter::<&Option<u8>>().find(|x| x.is_some()).map(|x| x.unwrap());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| *x)`
error: `find(..).map(..)` can be simplified as `find_map(..)`
- --> $DIR/manual_find_map.rs:35:28
+ --> $DIR/manual_find_map.rs:36:28
|
LL | iter::<&&Option<u8>>().find(|x| x.is_some()).map(|x| x.unwrap());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| **x)`
error: `find(..).map(..)` can be simplified as `find_map(..)`
- --> $DIR/manual_find_map.rs:36:27
+ --> $DIR/manual_find_map.rs:37:27
|
LL | iter::<Option<&u8>>().find(|x| x.is_some()).map(|x| x.cloned().unwrap());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x.cloned())`
error: `find(..).map(..)` can be simplified as `find_map(..)`
- --> $DIR/manual_find_map.rs:37:28
+ --> $DIR/manual_find_map.rs:38:28
|
LL | iter::<&Option<&u8>>().find(|x| x.is_some()).map(|x| x.cloned().unwrap());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x.cloned())`
error: `find(..).map(..)` can be simplified as `find_map(..)`
- --> $DIR/manual_find_map.rs:38:31
+ --> $DIR/manual_find_map.rs:39:31
|
LL | iter::<&Option<String>>().find(|x| x.is_some()).map(|x| x.as_deref().unwrap());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x.as_deref())`
error: `find(..).map(..)` can be simplified as `find_map(..)`
- --> $DIR/manual_find_map.rs:39:31
+ --> $DIR/manual_find_map.rs:40:31
|
LL | iter::<Option<&String>>().find(|&x| to_ref(x).is_some()).map(|y| to_ref(y).cloned().unwrap());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|y| to_ref(y).cloned())`
|
note: the suggestion might change the behavior of the program when merging `filter` and `map`, because this expression potentially contains side effects and will only execute once
- --> $DIR/manual_find_map.rs:39:41
+ --> $DIR/manual_find_map.rs:40:41
|
LL | iter::<Option<&String>>().find(|&x| to_ref(x).is_some()).map(|y| to_ref(y).cloned().unwrap());
| ^^^^^^^^^
error: `find(..).map(..)` can be simplified as `find_map(..)`
- --> $DIR/manual_find_map.rs:41:30
+ --> $DIR/manual_find_map.rs:42:30
|
LL | iter::<Result<u8, ()>>().find(|x| x.is_ok()).map(|x| x.unwrap());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x.ok())`
error: `find(..).map(..)` can be simplified as `find_map(..)`
- --> $DIR/manual_find_map.rs:42:31
+ --> $DIR/manual_find_map.rs:43:31
|
LL | iter::<&Result<u8, ()>>().find(|x| x.is_ok()).map(|x| x.unwrap());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x.ok())`
error: `find(..).map(..)` can be simplified as `find_map(..)`
- --> $DIR/manual_find_map.rs:43:32
+ --> $DIR/manual_find_map.rs:44:32
|
LL | iter::<&&Result<u8, ()>>().find(|x| x.is_ok()).map(|x| x.unwrap());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x.ok())`
error: `find(..).map(..)` can be simplified as `find_map(..)`
- --> $DIR/manual_find_map.rs:44:31
+ --> $DIR/manual_find_map.rs:45:31
|
LL | iter::<Result<&u8, ()>>().find(|x| x.is_ok()).map(|x| x.cloned().unwrap());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x.cloned().ok())`
error: `find(..).map(..)` can be simplified as `find_map(..)`
- --> $DIR/manual_find_map.rs:45:32
+ --> $DIR/manual_find_map.rs:46:32
|
LL | iter::<&Result<&u8, ()>>().find(|x| x.is_ok()).map(|x| x.cloned().unwrap());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x.cloned().ok())`
error: `find(..).map(..)` can be simplified as `find_map(..)`
- --> $DIR/manual_find_map.rs:46:35
+ --> $DIR/manual_find_map.rs:47:35
|
LL | iter::<&Result<String, ()>>().find(|x| x.is_ok()).map(|x| x.as_deref().unwrap());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x.as_deref().ok())`
error: `find(..).map(..)` can be simplified as `find_map(..)`
- --> $DIR/manual_find_map.rs:47:35
+ --> $DIR/manual_find_map.rs:48:35
|
LL | iter::<Result<&String, ()>>().find(|&x| to_ref(x).is_ok()).map(|y| to_ref(y).cloned().unwrap());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|y| to_ref(y).cloned().ok())`
|
note: the suggestion might change the behavior of the program when merging `filter` and `map`, because this expression potentially contains side effects and will only execute once
- --> $DIR/manual_find_map.rs:47:45
+ --> $DIR/manual_find_map.rs:48:45
|
LL | iter::<Result<&String, ()>>().find(|&x| to_ref(x).is_ok()).map(|y| to_ref(y).cloned().unwrap());
| ^^^^^^^^^
error: `find(..).map(..)` can be simplified as `find_map(..)`
- --> $DIR/manual_find_map.rs:95:10
+ --> $DIR/manual_find_map.rs:96:10
|
LL | .find(|f| f.option_field.is_some())
| __________^
@@ -197,7 +197,7 @@ LL | | .map(|f| f.option_field.clone().unwrap());
| |_________________________________________________^ help: try: `find_map(|f| f.option_field.clone())`
error: `find(..).map(..)` can be simplified as `find_map(..)`
- --> $DIR/manual_find_map.rs:100:10
+ --> $DIR/manual_find_map.rs:101:10
|
LL | .find(|f| f.ref_field.is_some())
| __________^
@@ -205,7 +205,7 @@ LL | | .map(|f| f.ref_field.cloned().unwrap());
| |_______________________________________________^ help: try: `find_map(|f| f.ref_field.cloned())`
error: `find(..).map(..)` can be simplified as `find_map(..)`
- --> $DIR/manual_find_map.rs:105:10
+ --> $DIR/manual_find_map.rs:106:10
|
LL | .find(|f| f.ref_field.is_some())
| __________^
@@ -213,7 +213,7 @@ LL | | .map(|f| f.ref_field.copied().unwrap());
| |_______________________________________________^ help: try: `find_map(|f| f.ref_field.copied())`
error: `find(..).map(..)` can be simplified as `find_map(..)`
- --> $DIR/manual_find_map.rs:110:10
+ --> $DIR/manual_find_map.rs:111:10
|
LL | .find(|f| f.result_field.is_ok())
| __________^
@@ -221,7 +221,7 @@ LL | | .map(|f| f.result_field.clone().unwrap());
| |_________________________________________________^ help: try: `find_map(|f| f.result_field.clone().ok())`
error: `find(..).map(..)` can be simplified as `find_map(..)`
- --> $DIR/manual_find_map.rs:115:10
+ --> $DIR/manual_find_map.rs:116:10
|
LL | .find(|f| f.result_field.is_ok())
| __________^
@@ -229,7 +229,7 @@ LL | | .map(|f| f.result_field.as_ref().unwrap());
| |__________________________________________________^ help: try: `find_map(|f| f.result_field.as_ref().ok())`
error: `find(..).map(..)` can be simplified as `find_map(..)`
- --> $DIR/manual_find_map.rs:120:10
+ --> $DIR/manual_find_map.rs:121:10
|
LL | .find(|f| f.result_field.is_ok())
| __________^
@@ -237,7 +237,7 @@ LL | | .map(|f| f.result_field.as_deref().unwrap());
| |____________________________________________________^ help: try: `find_map(|f| f.result_field.as_deref().ok())`
error: `find(..).map(..)` can be simplified as `find_map(..)`
- --> $DIR/manual_find_map.rs:125:10
+ --> $DIR/manual_find_map.rs:126:10
|
LL | .find(|f| f.result_field.is_ok())
| __________^
@@ -245,7 +245,7 @@ LL | | .map(|f| f.result_field.as_mut().unwrap());
| |__________________________________________________^ help: try: `find_map(|f| f.result_field.as_mut().ok())`
error: `find(..).map(..)` can be simplified as `find_map(..)`
- --> $DIR/manual_find_map.rs:130:10
+ --> $DIR/manual_find_map.rs:131:10
|
LL | .find(|f| f.result_field.is_ok())
| __________^
@@ -253,7 +253,7 @@ LL | | .map(|f| f.result_field.as_deref_mut().unwrap());
| |________________________________________________________^ help: try: `find_map(|f| f.result_field.as_deref_mut().ok())`
error: `find(..).map(..)` can be simplified as `find_map(..)`
- --> $DIR/manual_find_map.rs:135:10
+ --> $DIR/manual_find_map.rs:136:10
|
LL | .find(|f| f.result_field.is_ok())
| __________^
diff --git a/src/tools/clippy/tests/ui/manual_hash_one.fixed b/src/tools/clippy/tests/ui/manual_hash_one.fixed
new file mode 100644
index 000000000..edfd9c4a4
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_hash_one.fixed
@@ -0,0 +1,89 @@
+#![warn(clippy::manual_hash_one)]
+#![allow(clippy::needless_borrows_for_generic_args)]
+
+use std::hash::{BuildHasher, Hash, Hasher};
+
+fn returned(b: impl BuildHasher) -> u64 {
+
+
+ b.hash_one(&true)
+}
+
+fn unsized_receiver(b: impl BuildHasher, s: &str) {
+
+
+ let _ = b.hash_one(&s[4..10]);
+}
+
+fn owned_value(b: impl BuildHasher, v: Vec<u32>) -> Vec<u32> {
+
+
+ let _ = b.hash_one(&v);
+ v
+}
+
+fn reused_hasher(b: impl BuildHasher) {
+ let mut hasher = b.build_hasher();
+ true.hash(&mut hasher);
+ let _ = hasher.finish();
+ let _ = hasher.finish();
+}
+
+fn reused_hasher_in_return(b: impl BuildHasher) -> u64 {
+ let mut hasher = b.build_hasher();
+ true.hash(&mut hasher);
+ let _ = hasher.finish();
+ hasher.finish()
+}
+
+fn no_hash(b: impl BuildHasher) {
+ let mut hasher = b.build_hasher();
+ let _ = hasher.finish();
+}
+
+fn hash_twice(b: impl BuildHasher) {
+ let mut hasher = b.build_hasher();
+ true.hash(&mut hasher);
+ true.hash(&mut hasher);
+ let _ = hasher.finish();
+}
+
+fn other_hasher(b: impl BuildHasher) {
+ let mut other_hasher = b.build_hasher();
+
+ let mut hasher = b.build_hasher();
+ true.hash(&mut other_hasher);
+ let _ = hasher.finish();
+}
+
+fn finish_then_hash(b: impl BuildHasher) {
+ let mut hasher = b.build_hasher();
+ let _ = hasher.finish();
+ true.hash(&mut hasher);
+}
+
+fn in_macro(b: impl BuildHasher) {
+ macro_rules! m {
+ ($b:expr) => {{
+ let mut hasher = $b.build_hasher();
+ true.hash(&mut hasher);
+ let _ = hasher.finish();
+ }};
+ }
+
+ m!(b);
+}
+
+#[clippy::msrv = "1.70"]
+fn msrv_1_70(b: impl BuildHasher, v: impl Hash) {
+ let mut hasher = b.build_hasher();
+ v.hash(&mut hasher);
+ let _ = hasher.finish();
+}
+
+#[clippy::msrv = "1.71"]
+fn msrv_1_71(b: impl BuildHasher, v: impl Hash) {
+
+
+ let _ = b.hash_one(&v);
+}
diff --git a/src/tools/clippy/tests/ui/manual_hash_one.rs b/src/tools/clippy/tests/ui/manual_hash_one.rs
new file mode 100644
index 000000000..ee6152285
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_hash_one.rs
@@ -0,0 +1,89 @@
+#![warn(clippy::manual_hash_one)]
+#![allow(clippy::needless_borrows_for_generic_args)]
+
+use std::hash::{BuildHasher, Hash, Hasher};
+
+fn returned(b: impl BuildHasher) -> u64 {
+ let mut hasher = b.build_hasher();
+ true.hash(&mut hasher);
+ hasher.finish()
+}
+
+fn unsized_receiver(b: impl BuildHasher, s: &str) {
+ let mut hasher = b.build_hasher();
+ s[4..10].hash(&mut hasher);
+ let _ = hasher.finish();
+}
+
+fn owned_value(b: impl BuildHasher, v: Vec<u32>) -> Vec<u32> {
+ let mut hasher = b.build_hasher();
+ v.hash(&mut hasher);
+ let _ = hasher.finish();
+ v
+}
+
+fn reused_hasher(b: impl BuildHasher) {
+ let mut hasher = b.build_hasher();
+ true.hash(&mut hasher);
+ let _ = hasher.finish();
+ let _ = hasher.finish();
+}
+
+fn reused_hasher_in_return(b: impl BuildHasher) -> u64 {
+ let mut hasher = b.build_hasher();
+ true.hash(&mut hasher);
+ let _ = hasher.finish();
+ hasher.finish()
+}
+
+fn no_hash(b: impl BuildHasher) {
+ let mut hasher = b.build_hasher();
+ let _ = hasher.finish();
+}
+
+fn hash_twice(b: impl BuildHasher) {
+ let mut hasher = b.build_hasher();
+ true.hash(&mut hasher);
+ true.hash(&mut hasher);
+ let _ = hasher.finish();
+}
+
+fn other_hasher(b: impl BuildHasher) {
+ let mut other_hasher = b.build_hasher();
+
+ let mut hasher = b.build_hasher();
+ true.hash(&mut other_hasher);
+ let _ = hasher.finish();
+}
+
+fn finish_then_hash(b: impl BuildHasher) {
+ let mut hasher = b.build_hasher();
+ let _ = hasher.finish();
+ true.hash(&mut hasher);
+}
+
+fn in_macro(b: impl BuildHasher) {
+ macro_rules! m {
+ ($b:expr) => {{
+ let mut hasher = $b.build_hasher();
+ true.hash(&mut hasher);
+ let _ = hasher.finish();
+ }};
+ }
+
+ m!(b);
+}
+
+#[clippy::msrv = "1.70"]
+fn msrv_1_70(b: impl BuildHasher, v: impl Hash) {
+ let mut hasher = b.build_hasher();
+ v.hash(&mut hasher);
+ let _ = hasher.finish();
+}
+
+#[clippy::msrv = "1.71"]
+fn msrv_1_71(b: impl BuildHasher, v: impl Hash) {
+ let mut hasher = b.build_hasher();
+ v.hash(&mut hasher);
+ let _ = hasher.finish();
+}
diff --git a/src/tools/clippy/tests/ui/manual_hash_one.stderr b/src/tools/clippy/tests/ui/manual_hash_one.stderr
new file mode 100644
index 000000000..3ce6f41e1
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_hash_one.stderr
@@ -0,0 +1,56 @@
+error: manual implementation of `BuildHasher::hash_one`
+ --> $DIR/manual_hash_one.rs:9:5
+ |
+LL | hasher.finish()
+ | ^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::manual-hash-one` implied by `-D warnings`
+ = help: to override `-D warnings` add `#[allow(clippy::manual_hash_one)]`
+help: try
+ |
+LL ~
+LL ~
+LL ~ b.hash_one(&true)
+ |
+
+error: manual implementation of `BuildHasher::hash_one`
+ --> $DIR/manual_hash_one.rs:15:13
+ |
+LL | let _ = hasher.finish();
+ | ^^^^^^^^^^^^^^^
+ |
+help: try
+ |
+LL ~
+LL ~
+LL ~ let _ = b.hash_one(&s[4..10]);
+ |
+
+error: manual implementation of `BuildHasher::hash_one`
+ --> $DIR/manual_hash_one.rs:21:13
+ |
+LL | let _ = hasher.finish();
+ | ^^^^^^^^^^^^^^^
+ |
+help: try
+ |
+LL ~
+LL ~
+LL ~ let _ = b.hash_one(&v);
+ |
+
+error: manual implementation of `BuildHasher::hash_one`
+ --> $DIR/manual_hash_one.rs:88:13
+ |
+LL | let _ = hasher.finish();
+ | ^^^^^^^^^^^^^^^
+ |
+help: try
+ |
+LL ~
+LL ~
+LL ~ let _ = b.hash_one(&v);
+ |
+
+error: aborting due to 4 previous errors
+
diff --git a/src/tools/clippy/tests/ui/manual_is_ascii_check.fixed b/src/tools/clippy/tests/ui/manual_is_ascii_check.fixed
index 5be2dd280..9c4bd335a 100644
--- a/src/tools/clippy/tests/ui/manual_is_ascii_check.fixed
+++ b/src/tools/clippy/tests/ui/manual_is_ascii_check.fixed
@@ -33,6 +33,7 @@ fn msrv_1_23() {
assert!(matches!(b'1', b'0'..=b'9'));
assert!(matches!('X', 'A'..='Z'));
assert!(matches!('x', 'A'..='Z' | 'a'..='z'));
+ assert!(matches!('x', '0'..='9' | 'a'..='f' | 'A'..='F'));
}
#[clippy::msrv = "1.24"]
@@ -40,14 +41,17 @@ fn msrv_1_24() {
assert!(b'1'.is_ascii_digit());
assert!('X'.is_ascii_uppercase());
assert!('x'.is_ascii_alphabetic());
+ assert!('x'.is_ascii_hexdigit());
}
#[clippy::msrv = "1.46"]
fn msrv_1_46() {
const FOO: bool = matches!('x', '0'..='9');
+ const BAR: bool = matches!('x', '0'..='9' | 'a'..='f' | 'A'..='F');
}
#[clippy::msrv = "1.47"]
fn msrv_1_47() {
const FOO: bool = 'x'.is_ascii_digit();
+ const BAR: bool = 'x'.is_ascii_hexdigit();
}
diff --git a/src/tools/clippy/tests/ui/manual_is_ascii_check.rs b/src/tools/clippy/tests/ui/manual_is_ascii_check.rs
index f9249e22a..785943cd2 100644
--- a/src/tools/clippy/tests/ui/manual_is_ascii_check.rs
+++ b/src/tools/clippy/tests/ui/manual_is_ascii_check.rs
@@ -33,6 +33,7 @@ fn msrv_1_23() {
assert!(matches!(b'1', b'0'..=b'9'));
assert!(matches!('X', 'A'..='Z'));
assert!(matches!('x', 'A'..='Z' | 'a'..='z'));
+ assert!(matches!('x', '0'..='9' | 'a'..='f' | 'A'..='F'));
}
#[clippy::msrv = "1.24"]
@@ -40,14 +41,17 @@ fn msrv_1_24() {
assert!(matches!(b'1', b'0'..=b'9'));
assert!(matches!('X', 'A'..='Z'));
assert!(matches!('x', 'A'..='Z' | 'a'..='z'));
+ assert!(matches!('x', '0'..='9' | 'a'..='f' | 'A'..='F'));
}
#[clippy::msrv = "1.46"]
fn msrv_1_46() {
const FOO: bool = matches!('x', '0'..='9');
+ const BAR: bool = matches!('x', '0'..='9' | 'a'..='f' | 'A'..='F');
}
#[clippy::msrv = "1.47"]
fn msrv_1_47() {
const FOO: bool = matches!('x', '0'..='9');
+ const BAR: bool = matches!('x', '0'..='9' | 'a'..='f' | 'A'..='F');
}
diff --git a/src/tools/clippy/tests/ui/manual_is_ascii_check.stderr b/src/tools/clippy/tests/ui/manual_is_ascii_check.stderr
index e0fb46e59..f69522c5f 100644
--- a/src/tools/clippy/tests/ui/manual_is_ascii_check.stderr
+++ b/src/tools/clippy/tests/ui/manual_is_ascii_check.stderr
@@ -98,28 +98,40 @@ LL | ('A'..='Z').contains(cool_letter);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `cool_letter.is_ascii_uppercase()`
error: manual check for common ascii range
- --> $DIR/manual_is_ascii_check.rs:40:13
+ --> $DIR/manual_is_ascii_check.rs:41:13
|
LL | assert!(matches!(b'1', b'0'..=b'9'));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `b'1'.is_ascii_digit()`
error: manual check for common ascii range
- --> $DIR/manual_is_ascii_check.rs:41:13
+ --> $DIR/manual_is_ascii_check.rs:42:13
|
LL | assert!(matches!('X', 'A'..='Z'));
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'X'.is_ascii_uppercase()`
error: manual check for common ascii range
- --> $DIR/manual_is_ascii_check.rs:42:13
+ --> $DIR/manual_is_ascii_check.rs:43:13
|
LL | assert!(matches!('x', 'A'..='Z' | 'a'..='z'));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'x'.is_ascii_alphabetic()`
error: manual check for common ascii range
- --> $DIR/manual_is_ascii_check.rs:52:23
+ --> $DIR/manual_is_ascii_check.rs:44:13
+ |
+LL | assert!(matches!('x', '0'..='9' | 'a'..='f' | 'A'..='F'));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'x'.is_ascii_hexdigit()`
+
+error: manual check for common ascii range
+ --> $DIR/manual_is_ascii_check.rs:55:23
|
LL | const FOO: bool = matches!('x', '0'..='9');
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'x'.is_ascii_digit()`
-error: aborting due to 20 previous errors
+error: manual check for common ascii range
+ --> $DIR/manual_is_ascii_check.rs:56:23
+ |
+LL | const BAR: bool = matches!('x', '0'..='9' | 'a'..='f' | 'A'..='F');
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'x'.is_ascii_hexdigit()`
+
+error: aborting due to 22 previous errors
diff --git a/src/tools/clippy/tests/ui/manual_let_else.rs b/src/tools/clippy/tests/ui/manual_let_else.rs
index 6775fdc92..27717ab3a 100644
--- a/src/tools/clippy/tests/ui/manual_let_else.rs
+++ b/src/tools/clippy/tests/ui/manual_let_else.rs
@@ -35,9 +35,7 @@ fn fire() {
let v = if let Some(v) = g() {
//~^ ERROR: this could be rewritten as `let...else`
// Blocks around the identity should have no impact
- {
- { v }
- }
+ { { v } }
} else {
// Some computation should still make it fire
g();
diff --git a/src/tools/clippy/tests/ui/manual_let_else.stderr b/src/tools/clippy/tests/ui/manual_let_else.stderr
index 49dbd7615..2b6504a18 100644
--- a/src/tools/clippy/tests/ui/manual_let_else.stderr
+++ b/src/tools/clippy/tests/ui/manual_let_else.stderr
@@ -31,7 +31,7 @@ error: this could be rewritten as `let...else`
LL | / let v = if let Some(v) = g() {
LL | |
LL | | // Blocks around the identity should have no impact
-LL | | {
+LL | | { { v } }
... |
LL | | return;
LL | | };
@@ -47,25 +47,25 @@ LL + };
|
error: this could be rewritten as `let...else`
- --> $DIR/manual_let_else.rs:49:9
+ --> $DIR/manual_let_else.rs:47:9
|
LL | let v = if let Some(v_some) = g() { v_some } else { continue };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Some(v) = g() else { continue };`
error: this could be rewritten as `let...else`
- --> $DIR/manual_let_else.rs:51:9
+ --> $DIR/manual_let_else.rs:49:9
|
LL | let v = if let Some(v_some) = g() { v_some } else { break };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Some(v) = g() else { break };`
error: this could be rewritten as `let...else`
- --> $DIR/manual_let_else.rs:56:5
+ --> $DIR/manual_let_else.rs:54:5
|
LL | let v = if let Some(v_some) = g() { v_some } else { panic!() };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Some(v) = g() else { panic!() };`
error: this could be rewritten as `let...else`
- --> $DIR/manual_let_else.rs:60:5
+ --> $DIR/manual_let_else.rs:58:5
|
LL | / let v = if let Some(v_some) = g() {
LL | |
@@ -83,7 +83,7 @@ LL + };
|
error: this could be rewritten as `let...else`
- --> $DIR/manual_let_else.rs:68:5
+ --> $DIR/manual_let_else.rs:66:5
|
LL | / let v = if let Some(v_some) = g() {
LL | |
@@ -101,7 +101,7 @@ LL + };
|
error: this could be rewritten as `let...else`
- --> $DIR/manual_let_else.rs:76:5
+ --> $DIR/manual_let_else.rs:74:5
|
LL | / let v = if let Some(v_some) = g() {
LL | |
@@ -121,7 +121,7 @@ LL + };
|
error: this could be rewritten as `let...else`
- --> $DIR/manual_let_else.rs:87:5
+ --> $DIR/manual_let_else.rs:85:5
|
LL | / let v = if let Some(v_some) = g() {
LL | |
@@ -143,13 +143,13 @@ LL + };
|
error: this could be rewritten as `let...else`
- --> $DIR/manual_let_else.rs:98:5
+ --> $DIR/manual_let_else.rs:96:5
|
LL | let v = if let Some(v_some) = g() { v_some } else { if panic!() {} };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Some(v) = g() else { if panic!() {} };`
error: this could be rewritten as `let...else`
- --> $DIR/manual_let_else.rs:102:5
+ --> $DIR/manual_let_else.rs:100:5
|
LL | / let v = if let Some(v_some) = g() {
LL | |
@@ -170,7 +170,7 @@ LL + };
|
error: this could be rewritten as `let...else`
- --> $DIR/manual_let_else.rs:112:5
+ --> $DIR/manual_let_else.rs:110:5
|
LL | / let v = if let Some(v_some) = g() {
LL | |
@@ -191,7 +191,7 @@ LL + } };
|
error: this could be rewritten as `let...else`
- --> $DIR/manual_let_else.rs:122:5
+ --> $DIR/manual_let_else.rs:120:5
|
LL | / let v = if let Some(v_some) = g() {
LL | |
@@ -220,7 +220,7 @@ LL + };
|
error: this could be rewritten as `let...else`
- --> $DIR/manual_let_else.rs:140:5
+ --> $DIR/manual_let_else.rs:138:5
|
LL | / let (v, w) = if let Some(v_some) = g().map(|v| (v, 42)) {
LL | |
@@ -238,7 +238,7 @@ LL + };
|
error: this could be rewritten as `let...else`
- --> $DIR/manual_let_else.rs:148:5
+ --> $DIR/manual_let_else.rs:146:5
|
LL | / let (w, S { v }) = if let (Some(v_some), w_some) = (g().map(|_| S { v: 0 }), 0) {
LL | |
@@ -256,7 +256,7 @@ LL + };
|
error: this could be rewritten as `let...else`
- --> $DIR/manual_let_else.rs:158:13
+ --> $DIR/manual_let_else.rs:156:13
|
LL | let $n = if let Some(v) = $e { v } else { return };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Some($n) = g() else { return };`
@@ -267,19 +267,19 @@ LL | create_binding_if_some!(w, g());
= note: this error originates in the macro `create_binding_if_some` (in Nightly builds, run with -Z macro-backtrace for more info)
error: this could be rewritten as `let...else`
- --> $DIR/manual_let_else.rs:167:5
+ --> $DIR/manual_let_else.rs:165:5
|
LL | let v = if let Variant::A(a, 0) = e() { a } else { return };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Variant::A(v, 0) = e() else { return };`
error: this could be rewritten as `let...else`
- --> $DIR/manual_let_else.rs:171:5
+ --> $DIR/manual_let_else.rs:169:5
|
LL | let mut v = if let Variant::B(b) = e() { b } else { return };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Variant::B(mut v) = e() else { return };`
error: this could be rewritten as `let...else`
- --> $DIR/manual_let_else.rs:176:5
+ --> $DIR/manual_let_else.rs:174:5
|
LL | / let v = if let Ok(Some(Variant::B(b))) | Err(Some(Variant::A(b, _))) = nested {
LL | |
@@ -297,19 +297,19 @@ LL + };
|
error: this could be rewritten as `let...else`
- --> $DIR/manual_let_else.rs:183:5
+ --> $DIR/manual_let_else.rs:181:5
|
LL | let v = if let Variant::A(.., a) = e() { a } else { return };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Variant::A(.., v) = e() else { return };`
error: this could be rewritten as `let...else`
- --> $DIR/manual_let_else.rs:187:5
+ --> $DIR/manual_let_else.rs:185:5
|
LL | let w = if let (Some(v), ()) = (g(), ()) { v } else { return };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let (Some(w), ()) = (g(), ()) else { return };`
error: this could be rewritten as `let...else`
- --> $DIR/manual_let_else.rs:191:5
+ --> $DIR/manual_let_else.rs:189:5
|
LL | / let w = if let Some(S { v: x }) = Some(S { v: 0 }) {
LL | |
@@ -327,7 +327,7 @@ LL + };
|
error: this could be rewritten as `let...else`
- --> $DIR/manual_let_else.rs:199:5
+ --> $DIR/manual_let_else.rs:197:5
|
LL | / let v = if let Some(S { v: x }) = Some(S { v: 0 }) {
LL | |
@@ -345,7 +345,7 @@ LL + };
|
error: this could be rewritten as `let...else`
- --> $DIR/manual_let_else.rs:207:5
+ --> $DIR/manual_let_else.rs:205:5
|
LL | / let (x, S { v }, w) = if let Some(U { v, w, x }) = None::<U<S<()>>> {
LL | |
@@ -363,7 +363,7 @@ LL + };
|
error: this could be rewritten as `let...else`
- --> $DIR/manual_let_else.rs:324:5
+ --> $DIR/manual_let_else.rs:322:5
|
LL | / let _ = match ff {
LL | |
diff --git a/src/tools/clippy/tests/ui/manual_let_else_match.fixed b/src/tools/clippy/tests/ui/manual_let_else_match.fixed
index 09b713f04..588ba5edd 100644
--- a/src/tools/clippy/tests/ui/manual_let_else_match.fixed
+++ b/src/tools/clippy/tests/ui/manual_let_else_match.fixed
@@ -133,3 +133,7 @@ fn not_fire() {
[data @ .., 0, 0, 0, 0] | [data @ .., 0, 0] | [data @ ..] => data,
};
}
+
+fn issue11579() {
+ let Some(msg) = Some("hi") else { unreachable!("can't happen") };
+}
diff --git a/src/tools/clippy/tests/ui/manual_let_else_match.rs b/src/tools/clippy/tests/ui/manual_let_else_match.rs
index e6af47384..c37b5613f 100644
--- a/src/tools/clippy/tests/ui/manual_let_else_match.rs
+++ b/src/tools/clippy/tests/ui/manual_let_else_match.rs
@@ -170,3 +170,11 @@ fn not_fire() {
[data @ .., 0, 0, 0, 0] | [data @ .., 0, 0] | [data @ ..] => data,
};
}
+
+fn issue11579() {
+ let msg = match Some("hi") {
+ //~^ ERROR: this could be rewritten as `let...else`
+ Some(m) => m,
+ _ => unreachable!("can't happen"),
+ };
+}
diff --git a/src/tools/clippy/tests/ui/manual_let_else_match.stderr b/src/tools/clippy/tests/ui/manual_let_else_match.stderr
index 8ca2c8407..18bfe324b 100644
--- a/src/tools/clippy/tests/ui/manual_let_else_match.stderr
+++ b/src/tools/clippy/tests/ui/manual_let_else_match.stderr
@@ -92,5 +92,15 @@ LL | | _ => return,
LL | | };
| |______^ help: consider writing: `let ([data @ .., 0, 0, 0, 0] | [data @ .., 0, 0] | [data @ .., 0]) = data.as_slice() else { return };`
-error: aborting due to 9 previous errors
+error: this could be rewritten as `let...else`
+ --> $DIR/manual_let_else_match.rs:175:5
+ |
+LL | / let msg = match Some("hi") {
+LL | |
+LL | | Some(m) => m,
+LL | | _ => unreachable!("can't happen"),
+LL | | };
+ | |______^ help: consider writing: `let Some(msg) = Some("hi") else { unreachable!("can't happen") };`
+
+error: aborting due to 10 previous errors
diff --git a/src/tools/clippy/tests/ui/manual_map_option_2.fixed b/src/tools/clippy/tests/ui/manual_map_option_2.fixed
index 513f6e323..f5bb4e0af 100644
--- a/src/tools/clippy/tests/ui/manual_map_option_2.fixed
+++ b/src/tools/clippy/tests/ui/manual_map_option_2.fixed
@@ -42,9 +42,7 @@ fn main() {
// Lint. `s` is captured by reference, so no lifetime issues.
let s = Some(String::new());
- let _ = s.as_ref().map(|x| {
- if let Some(ref s) = s { (x.clone(), s) } else { panic!() }
- });
+ let _ = s.as_ref().map(|x| { if let Some(ref s) = s { (x.clone(), s) } else { panic!() } });
// Issue #7820
unsafe fn f(x: u32) -> u32 {
diff --git a/src/tools/clippy/tests/ui/manual_map_option_2.rs b/src/tools/clippy/tests/ui/manual_map_option_2.rs
index fd186743f..cbc2356e0 100644
--- a/src/tools/clippy/tests/ui/manual_map_option_2.rs
+++ b/src/tools/clippy/tests/ui/manual_map_option_2.rs
@@ -46,9 +46,7 @@ fn main() {
// Lint. `s` is captured by reference, so no lifetime issues.
let s = Some(String::new());
let _ = match &s {
- Some(x) => Some({
- if let Some(ref s) = s { (x.clone(), s) } else { panic!() }
- }),
+ Some(x) => Some({ if let Some(ref s) = s { (x.clone(), s) } else { panic!() } }),
None => None,
};
diff --git a/src/tools/clippy/tests/ui/manual_map_option_2.stderr b/src/tools/clippy/tests/ui/manual_map_option_2.stderr
index bf242c041..d3754f22d 100644
--- a/src/tools/clippy/tests/ui/manual_map_option_2.stderr
+++ b/src/tools/clippy/tests/ui/manual_map_option_2.stderr
@@ -26,22 +26,13 @@ error: manual implementation of `Option::map`
|
LL | let _ = match &s {
| _____________^
-LL | | Some(x) => Some({
-LL | | if let Some(ref s) = s { (x.clone(), s) } else { panic!() }
-LL | | }),
+LL | | Some(x) => Some({ if let Some(ref s) = s { (x.clone(), s) } else { panic!() } }),
LL | | None => None,
LL | | };
- | |_____^
- |
-help: try
- |
-LL ~ let _ = s.as_ref().map(|x| {
-LL + if let Some(ref s) = s { (x.clone(), s) } else { panic!() }
-LL ~ });
- |
+ | |_____^ help: try: `s.as_ref().map(|x| { if let Some(ref s) = s { (x.clone(), s) } else { panic!() } })`
error: manual implementation of `Option::map`
- --> $DIR/manual_map_option_2.rs:60:17
+ --> $DIR/manual_map_option_2.rs:58:17
|
LL | let _ = match Some(0) {
| _________________^
@@ -51,7 +42,7 @@ LL | | };
| |_________^ help: try: `Some(0).map(|x| f(x))`
error: manual implementation of `Option::map`
- --> $DIR/manual_map_option_2.rs:65:13
+ --> $DIR/manual_map_option_2.rs:63:13
|
LL | let _ = match Some(0) {
| _____________^
@@ -61,7 +52,7 @@ LL | | };
| |_____^ help: try: `Some(0).map(|x| unsafe { f(x) })`
error: manual implementation of `Option::map`
- --> $DIR/manual_map_option_2.rs:69:13
+ --> $DIR/manual_map_option_2.rs:67:13
|
LL | let _ = match Some(0) {
| _____________^
diff --git a/src/tools/clippy/tests/ui/manual_non_exhaustive_enum.rs b/src/tools/clippy/tests/ui/manual_non_exhaustive_enum.rs
index 0e439dabf..e32ba8631 100644
--- a/src/tools/clippy/tests/ui/manual_non_exhaustive_enum.rs
+++ b/src/tools/clippy/tests/ui/manual_non_exhaustive_enum.rs
@@ -10,10 +10,9 @@ enum E {
_C,
}
-// user forgot to remove the marker
+// if the user explicitly marks as nonexhaustive we shouldn't warn them
#[non_exhaustive]
enum Ep {
- //~^ ERROR: this seems like a manual implementation of the non-exhaustive pattern
A,
B,
#[doc(hidden)]
diff --git a/src/tools/clippy/tests/ui/manual_non_exhaustive_enum.stderr b/src/tools/clippy/tests/ui/manual_non_exhaustive_enum.stderr
index ce7e21c94..7361a4a2c 100644
--- a/src/tools/clippy/tests/ui/manual_non_exhaustive_enum.stderr
+++ b/src/tools/clippy/tests/ui/manual_non_exhaustive_enum.stderr
@@ -22,23 +22,5 @@ LL | _C,
= note: `-D clippy::manual-non-exhaustive` implied by `-D warnings`
= help: to override `-D warnings` add `#[allow(clippy::manual_non_exhaustive)]`
-error: this seems like a manual implementation of the non-exhaustive pattern
- --> $DIR/manual_non_exhaustive_enum.rs:15:1
- |
-LL | / enum Ep {
-LL | |
-LL | | A,
-LL | | B,
-LL | | #[doc(hidden)]
-LL | | _C,
-LL | | }
- | |_^
- |
-help: remove this variant
- --> $DIR/manual_non_exhaustive_enum.rs:20:5
- |
-LL | _C,
- | ^^
-
-error: aborting due to 2 previous errors
+error: aborting due to previous error
diff --git a/src/tools/clippy/tests/ui/manual_string_new.fixed b/src/tools/clippy/tests/ui/manual_string_new.fixed
index 273be4e0f..2d4c5a029 100644
--- a/src/tools/clippy/tests/ui/manual_string_new.fixed
+++ b/src/tools/clippy/tests/ui/manual_string_new.fixed
@@ -1,4 +1,5 @@
#![warn(clippy::manual_string_new)]
+#![allow(clippy::unnecessary_fallible_conversions)]
macro_rules! create_strings_from_macro {
// When inside a macro, nothing should warn to prevent false positives.
diff --git a/src/tools/clippy/tests/ui/manual_string_new.rs b/src/tools/clippy/tests/ui/manual_string_new.rs
index 0d5514fc8..20f0be6aa 100644
--- a/src/tools/clippy/tests/ui/manual_string_new.rs
+++ b/src/tools/clippy/tests/ui/manual_string_new.rs
@@ -1,4 +1,5 @@
#![warn(clippy::manual_string_new)]
+#![allow(clippy::unnecessary_fallible_conversions)]
macro_rules! create_strings_from_macro {
// When inside a macro, nothing should warn to prevent false positives.
diff --git a/src/tools/clippy/tests/ui/manual_string_new.stderr b/src/tools/clippy/tests/ui/manual_string_new.stderr
index 399652d3f..cb2d78c39 100644
--- a/src/tools/clippy/tests/ui/manual_string_new.stderr
+++ b/src/tools/clippy/tests/ui/manual_string_new.stderr
@@ -1,5 +1,5 @@
error: empty String is being created manually
- --> $DIR/manual_string_new.rs:13:13
+ --> $DIR/manual_string_new.rs:14:13
|
LL | let _ = "".to_string();
| ^^^^^^^^^^^^^^ help: consider using: `String::new()`
@@ -8,49 +8,49 @@ LL | let _ = "".to_string();
= help: to override `-D warnings` add `#[allow(clippy::manual_string_new)]`
error: empty String is being created manually
- --> $DIR/manual_string_new.rs:16:13
+ --> $DIR/manual_string_new.rs:17:13
|
LL | let _ = "".to_owned();
| ^^^^^^^^^^^^^ help: consider using: `String::new()`
error: empty String is being created manually
- --> $DIR/manual_string_new.rs:19:21
+ --> $DIR/manual_string_new.rs:20:21
|
LL | let _: String = "".into();
| ^^^^^^^^^ help: consider using: `String::new()`
error: empty String is being created manually
- --> $DIR/manual_string_new.rs:26:13
+ --> $DIR/manual_string_new.rs:27:13
|
LL | let _ = String::from("");
| ^^^^^^^^^^^^^^^^ help: consider using: `String::new()`
error: empty String is being created manually
- --> $DIR/manual_string_new.rs:27:13
+ --> $DIR/manual_string_new.rs:28:13
|
LL | let _ = <String>::from("");
| ^^^^^^^^^^^^^^^^^^ help: consider using: `String::new()`
error: empty String is being created manually
- --> $DIR/manual_string_new.rs:32:13
+ --> $DIR/manual_string_new.rs:33:13
|
LL | let _ = String::try_from("").unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `String::new()`
error: empty String is being created manually
- --> $DIR/manual_string_new.rs:38:21
+ --> $DIR/manual_string_new.rs:39:21
|
LL | let _: String = From::from("");
| ^^^^^^^^^^^^^^ help: consider using: `String::new()`
error: empty String is being created manually
- --> $DIR/manual_string_new.rs:43:21
+ --> $DIR/manual_string_new.rs:44:21
|
LL | let _: String = TryFrom::try_from("").unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `String::new()`
error: empty String is being created manually
- --> $DIR/manual_string_new.rs:46:21
+ --> $DIR/manual_string_new.rs:47:21
|
LL | let _: String = TryFrom::try_from("").expect("this should warn");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `String::new()`
diff --git a/src/tools/clippy/tests/ui/map_identity.fixed b/src/tools/clippy/tests/ui/map_identity.fixed
index cc40b1620..62b0ba018 100644
--- a/src/tools/clippy/tests/ui/map_identity.fixed
+++ b/src/tools/clippy/tests/ui/map_identity.fixed
@@ -17,6 +17,33 @@ fn main() {
});
let _: Result<u32, u32> = Ok(1);
let _: Result<u32, u32> = Ok(1).map_err(|a: u32| a * 42);
+ // : u32 guides type inference
+ let _ = Ok(1).map_err(|a: u32| a);
+ let _ = Ok(1).map_err(std::convert::identity::<u32>);
+}
+
+fn issue7189() {
+ // should lint
+ let x = [(1, 2), (3, 4)];
+ let _ = x.iter();
+ let _ = x.iter();
+ let _ = x.iter();
+
+ let y = [(1, 2, (3, (4,))), (5, 6, (7, (8,)))];
+ let _ = y.iter();
+
+ // should not lint
+ let _ = x.iter().map(|(x, y)| (x, y, y));
+ let _ = x.iter().map(|(x, _y)| (x,));
+ let _ = x.iter().map(|(x, _)| (x,));
+ let _ = x.iter().map(|(x, ..)| (x,));
+ let _ = y.iter().map(|(x, y, (z, _))| (x, y, (z, z)));
+ let _ = y
+ .iter()
+ .map(|(x, y, (z, _)): &(i32, i32, (i32, (i32,)))| (x, y, (z, z)));
+ let _ = y
+ .iter()
+ .map(|(x, y, (z, (w,))): &(i32, i32, (i32, (i32,)))| (x, y, (z, (w,))));
}
fn not_identity(x: &u16) -> u16 {
diff --git a/src/tools/clippy/tests/ui/map_identity.rs b/src/tools/clippy/tests/ui/map_identity.rs
index 97a91aea6..b7f4c99f2 100644
--- a/src/tools/clippy/tests/ui/map_identity.rs
+++ b/src/tools/clippy/tests/ui/map_identity.rs
@@ -19,6 +19,35 @@ fn main() {
});
let _: Result<u32, u32> = Ok(1).map_err(|a| a);
let _: Result<u32, u32> = Ok(1).map_err(|a: u32| a * 42);
+ // : u32 guides type inference
+ let _ = Ok(1).map_err(|a: u32| a);
+ let _ = Ok(1).map_err(std::convert::identity::<u32>);
+}
+
+fn issue7189() {
+ // should lint
+ let x = [(1, 2), (3, 4)];
+ let _ = x.iter().map(|(x, y)| (x, y));
+ let _ = x.iter().map(|(x, y)| {
+ return (x, y);
+ });
+ let _ = x.iter().map(|(x, y)| return (x, y));
+
+ let y = [(1, 2, (3, (4,))), (5, 6, (7, (8,)))];
+ let _ = y.iter().map(|(x, y, (z, (w,)))| (x, y, (z, (w,))));
+
+ // should not lint
+ let _ = x.iter().map(|(x, y)| (x, y, y));
+ let _ = x.iter().map(|(x, _y)| (x,));
+ let _ = x.iter().map(|(x, _)| (x,));
+ let _ = x.iter().map(|(x, ..)| (x,));
+ let _ = y.iter().map(|(x, y, (z, _))| (x, y, (z, z)));
+ let _ = y
+ .iter()
+ .map(|(x, y, (z, _)): &(i32, i32, (i32, (i32,)))| (x, y, (z, z)));
+ let _ = y
+ .iter()
+ .map(|(x, y, (z, (w,))): &(i32, i32, (i32, (i32,)))| (x, y, (z, (w,))));
}
fn not_identity(x: &u16) -> u16 {
diff --git a/src/tools/clippy/tests/ui/map_identity.stderr b/src/tools/clippy/tests/ui/map_identity.stderr
index 8942fd7c0..4ca24b0b0 100644
--- a/src/tools/clippy/tests/ui/map_identity.stderr
+++ b/src/tools/clippy/tests/ui/map_identity.stderr
@@ -40,5 +40,32 @@ error: unnecessary map of the identity function
LL | let _: Result<u32, u32> = Ok(1).map_err(|a| a);
| ^^^^^^^^^^^^^^^ help: remove the call to `map_err`
-error: aborting due to 6 previous errors
+error: unnecessary map of the identity function
+ --> $DIR/map_identity.rs:30:21
+ |
+LL | let _ = x.iter().map(|(x, y)| (x, y));
+ | ^^^^^^^^^^^^^^^^^^^^^ help: remove the call to `map`
+
+error: unnecessary map of the identity function
+ --> $DIR/map_identity.rs:31:21
+ |
+LL | let _ = x.iter().map(|(x, y)| {
+ | _____________________^
+LL | | return (x, y);
+LL | | });
+ | |______^ help: remove the call to `map`
+
+error: unnecessary map of the identity function
+ --> $DIR/map_identity.rs:34:21
+ |
+LL | let _ = x.iter().map(|(x, y)| return (x, y));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove the call to `map`
+
+error: unnecessary map of the identity function
+ --> $DIR/map_identity.rs:37:21
+ |
+LL | let _ = y.iter().map(|(x, y, (z, (w,)))| (x, y, (z, (w,))));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove the call to `map`
+
+error: aborting due to 10 previous errors
diff --git a/src/tools/clippy/tests/ui/match_same_arms_non_exhaustive.rs b/src/tools/clippy/tests/ui/match_same_arms_non_exhaustive.rs
index 1ee048bf7..5c277f925 100644
--- a/src/tools/clippy/tests/ui/match_same_arms_non_exhaustive.rs
+++ b/src/tools/clippy/tests/ui/match_same_arms_non_exhaustive.rs
@@ -4,14 +4,18 @@
//@no-rustfix
use std::sync::atomic::Ordering; // #[non_exhaustive] enum
+fn repeat() -> ! {
+ panic!()
+}
+
pub fn f(x: Ordering) {
+ #[deny(non_exhaustive_omitted_patterns)]
match x {
Ordering::Relaxed => println!("relaxed"),
Ordering::Release => println!("release"),
Ordering::Acquire => println!("acquire"),
- Ordering::AcqRel | Ordering::SeqCst => panic!(),
- #[deny(non_exhaustive_omitted_patterns)]
- _ => panic!(),
+ Ordering::AcqRel | Ordering::SeqCst => repeat(),
+ _ => repeat(),
}
}
@@ -25,8 +29,8 @@ mod f {
Ordering::Relaxed => println!("relaxed"),
Ordering::Release => println!("release"),
Ordering::Acquire => println!("acquire"),
- Ordering::AcqRel | Ordering::SeqCst => panic!(),
- _ => panic!(),
+ Ordering::AcqRel | Ordering::SeqCst => repeat(),
+ _ => repeat(),
}
}
}
@@ -38,9 +42,9 @@ pub fn g(x: Ordering) {
Ordering::Relaxed => println!("relaxed"),
Ordering::Release => println!("release"),
Ordering::Acquire => println!("acquire"),
- Ordering::AcqRel | Ordering::SeqCst => panic!(),
+ Ordering::AcqRel | Ordering::SeqCst => repeat(),
//~^ ERROR: this match arm has an identical body to the `_` wildcard arm
- _ => panic!(),
+ _ => repeat(),
}
}
@@ -52,9 +56,9 @@ mod g {
Ordering::Relaxed => println!("relaxed"),
Ordering::Release => println!("release"),
Ordering::Acquire => println!("acquire"),
- Ordering::AcqRel | Ordering::SeqCst => panic!(),
+ Ordering::AcqRel | Ordering::SeqCst => repeat(),
//~^ ERROR: this match arm has an identical body to the `_` wildcard arm
- _ => panic!(),
+ _ => repeat(),
}
}
}
diff --git a/src/tools/clippy/tests/ui/match_same_arms_non_exhaustive.stderr b/src/tools/clippy/tests/ui/match_same_arms_non_exhaustive.stderr
index a03953633..ae6b02ab1 100644
--- a/src/tools/clippy/tests/ui/match_same_arms_non_exhaustive.stderr
+++ b/src/tools/clippy/tests/ui/match_same_arms_non_exhaustive.stderr
@@ -1,29 +1,29 @@
error: this match arm has an identical body to the `_` wildcard arm
- --> $DIR/match_same_arms_non_exhaustive.rs:41:9
+ --> $DIR/match_same_arms_non_exhaustive.rs:45:9
|
-LL | Ordering::AcqRel | Ordering::SeqCst => panic!(),
+LL | Ordering::AcqRel | Ordering::SeqCst => repeat(),
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try removing the arm
|
= help: or try changing either arm body
note: `_` wildcard arm here
- --> $DIR/match_same_arms_non_exhaustive.rs:43:9
+ --> $DIR/match_same_arms_non_exhaustive.rs:47:9
|
-LL | _ => panic!(),
+LL | _ => repeat(),
| ^^^^^^^^^^^^^
= note: `-D clippy::match-same-arms` implied by `-D warnings`
= help: to override `-D warnings` add `#[allow(clippy::match_same_arms)]`
error: this match arm has an identical body to the `_` wildcard arm
- --> $DIR/match_same_arms_non_exhaustive.rs:55:13
+ --> $DIR/match_same_arms_non_exhaustive.rs:59:13
|
-LL | Ordering::AcqRel | Ordering::SeqCst => panic!(),
+LL | Ordering::AcqRel | Ordering::SeqCst => repeat(),
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try removing the arm
|
= help: or try changing either arm body
note: `_` wildcard arm here
- --> $DIR/match_same_arms_non_exhaustive.rs:57:13
+ --> $DIR/match_same_arms_non_exhaustive.rs:61:13
|
-LL | _ => panic!(),
+LL | _ => repeat(),
| ^^^^^^^^^^^^^
error: aborting due to 2 previous errors
diff --git a/src/tools/clippy/tests/ui/min_ident_chars.rs b/src/tools/clippy/tests/ui/min_ident_chars.rs
index 030863ca0..f99c35d5c 100644
--- a/src/tools/clippy/tests/ui/min_ident_chars.rs
+++ b/src/tools/clippy/tests/ui/min_ident_chars.rs
@@ -1,5 +1,6 @@
//@aux-build:proc_macros.rs
#![allow(irrefutable_let_patterns, nonstandard_style, unused)]
+#![allow(clippy::struct_field_names)]
#![warn(clippy::min_ident_chars)]
extern crate proc_macros;
diff --git a/src/tools/clippy/tests/ui/min_ident_chars.stderr b/src/tools/clippy/tests/ui/min_ident_chars.stderr
index 253636cf9..e4181157e 100644
--- a/src/tools/clippy/tests/ui/min_ident_chars.stderr
+++ b/src/tools/clippy/tests/ui/min_ident_chars.stderr
@@ -1,5 +1,5 @@
error: this ident consists of a single char
- --> $DIR/min_ident_chars.rs:8:8
+ --> $DIR/min_ident_chars.rs:9:8
|
LL | struct A {
| ^
@@ -8,169 +8,169 @@ LL | struct A {
= help: to override `-D warnings` add `#[allow(clippy::min_ident_chars)]`
error: this ident consists of a single char
- --> $DIR/min_ident_chars.rs:9:5
+ --> $DIR/min_ident_chars.rs:10:5
|
LL | a: u32,
| ^
error: this ident consists of a single char
- --> $DIR/min_ident_chars.rs:11:5
+ --> $DIR/min_ident_chars.rs:12:5
|
LL | A: u32,
| ^
error: this ident consists of a single char
- --> $DIR/min_ident_chars.rs:12:5
+ --> $DIR/min_ident_chars.rs:13:5
|
LL | I: u32,
| ^
error: this ident consists of a single char
- --> $DIR/min_ident_chars.rs:15:8
+ --> $DIR/min_ident_chars.rs:16:8
|
LL | struct B(u32);
| ^
error: this ident consists of a single char
- --> $DIR/min_ident_chars.rs:17:8
+ --> $DIR/min_ident_chars.rs:18:8
|
LL | struct O {
| ^
error: this ident consists of a single char
- --> $DIR/min_ident_chars.rs:18:5
+ --> $DIR/min_ident_chars.rs:19:5
|
LL | o: u32,
| ^
error: this ident consists of a single char
- --> $DIR/min_ident_chars.rs:23:6
+ --> $DIR/min_ident_chars.rs:24:6
|
LL | enum C {
| ^
error: this ident consists of a single char
- --> $DIR/min_ident_chars.rs:24:5
+ --> $DIR/min_ident_chars.rs:25:5
|
LL | D,
| ^
error: this ident consists of a single char
- --> $DIR/min_ident_chars.rs:25:5
+ --> $DIR/min_ident_chars.rs:26:5
|
LL | E,
| ^
error: this ident consists of a single char
- --> $DIR/min_ident_chars.rs:26:5
+ --> $DIR/min_ident_chars.rs:27:5
|
LL | F,
| ^
error: this ident consists of a single char
- --> $DIR/min_ident_chars.rs:50:9
+ --> $DIR/min_ident_chars.rs:51:9
|
LL | let h = 1;
| ^
error: this ident consists of a single char
- --> $DIR/min_ident_chars.rs:51:9
+ --> $DIR/min_ident_chars.rs:52:9
|
LL | let e = 2;
| ^
error: this ident consists of a single char
- --> $DIR/min_ident_chars.rs:52:9
+ --> $DIR/min_ident_chars.rs:53:9
|
LL | let l = 3;
| ^
error: this ident consists of a single char
- --> $DIR/min_ident_chars.rs:53:9
+ --> $DIR/min_ident_chars.rs:54:9
|
LL | let l = 4;
| ^
error: this ident consists of a single char
- --> $DIR/min_ident_chars.rs:54:9
+ --> $DIR/min_ident_chars.rs:55:9
|
LL | let o = 6;
| ^
error: this ident consists of a single char
- --> $DIR/min_ident_chars.rs:58:10
+ --> $DIR/min_ident_chars.rs:59:10
|
LL | let (h, o, w) = (1, 2, 3);
| ^
error: this ident consists of a single char
- --> $DIR/min_ident_chars.rs:58:13
+ --> $DIR/min_ident_chars.rs:59:13
|
LL | let (h, o, w) = (1, 2, 3);
| ^
error: this ident consists of a single char
- --> $DIR/min_ident_chars.rs:59:10
+ --> $DIR/min_ident_chars.rs:60:10
|
LL | for (a, (r, e)) in (0..1000).enumerate().enumerate() {}
| ^
error: this ident consists of a single char
- --> $DIR/min_ident_chars.rs:59:14
+ --> $DIR/min_ident_chars.rs:60:14
|
LL | for (a, (r, e)) in (0..1000).enumerate().enumerate() {}
| ^
error: this ident consists of a single char
- --> $DIR/min_ident_chars.rs:59:17
+ --> $DIR/min_ident_chars.rs:60:17
|
LL | for (a, (r, e)) in (0..1000).enumerate().enumerate() {}
| ^
error: this ident consists of a single char
- --> $DIR/min_ident_chars.rs:61:16
+ --> $DIR/min_ident_chars.rs:62:16
|
LL | while let (d, o, _i, n, g) = (true, true, false, false, true) {}
| ^
error: this ident consists of a single char
- --> $DIR/min_ident_chars.rs:61:19
+ --> $DIR/min_ident_chars.rs:62:19
|
LL | while let (d, o, _i, n, g) = (true, true, false, false, true) {}
| ^
error: this ident consists of a single char
- --> $DIR/min_ident_chars.rs:61:29
+ --> $DIR/min_ident_chars.rs:62:29
|
LL | while let (d, o, _i, n, g) = (true, true, false, false, true) {}
| ^
error: this ident consists of a single char
- --> $DIR/min_ident_chars.rs:65:9
+ --> $DIR/min_ident_chars.rs:66:9
|
LL | let o = 1;
| ^
error: this ident consists of a single char
- --> $DIR/min_ident_chars.rs:66:9
+ --> $DIR/min_ident_chars.rs:67:9
|
LL | let o = O { o };
| ^
error: this ident consists of a single char
- --> $DIR/min_ident_chars.rs:80:4
+ --> $DIR/min_ident_chars.rs:81:4
|
LL | fn b() {}
| ^
error: this ident consists of a single char
- --> $DIR/min_ident_chars.rs:81:21
+ --> $DIR/min_ident_chars.rs:82:21
|
LL | fn wrong_pythagoras(a: f32, b: f32) -> f32 {
| ^
error: this ident consists of a single char
- --> $DIR/min_ident_chars.rs:81:29
+ --> $DIR/min_ident_chars.rs:82:29
|
LL | fn wrong_pythagoras(a: f32, b: f32) -> f32 {
| ^
diff --git a/src/tools/clippy/tests/ui/min_rust_version_invalid_attr.rs b/src/tools/clippy/tests/ui/min_rust_version_invalid_attr.rs
index c8a0d6641..3917bb9e0 100644
--- a/src/tools/clippy/tests/ui/min_rust_version_invalid_attr.rs
+++ b/src/tools/clippy/tests/ui/min_rust_version_invalid_attr.rs
@@ -11,13 +11,12 @@ fn outer_attr() {}
mod multiple {
#![clippy::msrv = "1.40"]
#![clippy::msrv = "=1.35.0"]
- //~^ ERROR: `msrv` is defined multiple times
#![clippy::msrv = "1.10.1"]
- //~^ ERROR: `msrv` is defined multiple times
+ //~^ ERROR: `clippy::msrv` is defined multiple times
mod foo {
#![clippy::msrv = "1"]
#![clippy::msrv = "1.0.0"]
- //~^ ERROR: `msrv` is defined multiple times
+ //~^ ERROR: `clippy::msrv` is defined multiple times
}
}
diff --git a/src/tools/clippy/tests/ui/min_rust_version_invalid_attr.stderr b/src/tools/clippy/tests/ui/min_rust_version_invalid_attr.stderr
index 8d4071e25..cf8392f03 100644
--- a/src/tools/clippy/tests/ui/min_rust_version_invalid_attr.stderr
+++ b/src/tools/clippy/tests/ui/min_rust_version_invalid_attr.stderr
@@ -10,20 +10,8 @@ error: `invalid.version` is not a valid Rust version
LL | #[clippy::msrv = "invalid.version"]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-error: `msrv` is defined multiple times
- --> $DIR/min_rust_version_invalid_attr.rs:13:5
- |
-LL | #![clippy::msrv = "=1.35.0"]
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- |
-note: first definition found here
- --> $DIR/min_rust_version_invalid_attr.rs:12:5
- |
-LL | #![clippy::msrv = "1.40"]
- | ^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: `msrv` is defined multiple times
- --> $DIR/min_rust_version_invalid_attr.rs:15:5
+error: `clippy::msrv` is defined multiple times
+ --> $DIR/min_rust_version_invalid_attr.rs:14:5
|
LL | #![clippy::msrv = "1.10.1"]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -34,17 +22,17 @@ note: first definition found here
LL | #![clippy::msrv = "1.40"]
| ^^^^^^^^^^^^^^^^^^^^^^^^^
-error: `msrv` is defined multiple times
- --> $DIR/min_rust_version_invalid_attr.rs:20:9
+error: `clippy::msrv` is defined multiple times
+ --> $DIR/min_rust_version_invalid_attr.rs:19:9
|
LL | #![clippy::msrv = "1.0.0"]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: first definition found here
- --> $DIR/min_rust_version_invalid_attr.rs:19:9
+ --> $DIR/min_rust_version_invalid_attr.rs:18:9
|
LL | #![clippy::msrv = "1"]
| ^^^^^^^^^^^^^^^^^^^^^^
-error: aborting due to 5 previous errors
+error: aborting due to 4 previous errors
diff --git a/src/tools/clippy/tests/ui/misnamed_getters.fixed b/src/tools/clippy/tests/ui/misnamed_getters.fixed
index 2a7a2067e..70af604b2 100644
--- a/src/tools/clippy/tests/ui/misnamed_getters.fixed
+++ b/src/tools/clippy/tests/ui/misnamed_getters.fixed
@@ -1,4 +1,5 @@
#![allow(unused)]
+#![allow(clippy::struct_field_names)]
#![warn(clippy::misnamed_getters)]
struct A {
diff --git a/src/tools/clippy/tests/ui/misnamed_getters.rs b/src/tools/clippy/tests/ui/misnamed_getters.rs
index 56ddc46c4..23c3e7bc5 100644
--- a/src/tools/clippy/tests/ui/misnamed_getters.rs
+++ b/src/tools/clippy/tests/ui/misnamed_getters.rs
@@ -1,4 +1,5 @@
#![allow(unused)]
+#![allow(clippy::struct_field_names)]
#![warn(clippy::misnamed_getters)]
struct A {
diff --git a/src/tools/clippy/tests/ui/misnamed_getters.stderr b/src/tools/clippy/tests/ui/misnamed_getters.stderr
index aadec6549..120a3f311 100644
--- a/src/tools/clippy/tests/ui/misnamed_getters.stderr
+++ b/src/tools/clippy/tests/ui/misnamed_getters.stderr
@@ -1,5 +1,5 @@
error: getter function appears to return the wrong field
- --> $DIR/misnamed_getters.rs:11:5
+ --> $DIR/misnamed_getters.rs:12:5
|
LL | / fn a(&self) -> &u8 {
LL | |
@@ -13,7 +13,7 @@ LL | | }
= help: to override `-D warnings` add `#[allow(clippy::misnamed_getters)]`
error: getter function appears to return the wrong field
- --> $DIR/misnamed_getters.rs:16:5
+ --> $DIR/misnamed_getters.rs:17:5
|
LL | / fn a_mut(&mut self) -> &mut u8 {
LL | |
@@ -23,7 +23,7 @@ LL | | }
| |_____^
error: getter function appears to return the wrong field
- --> $DIR/misnamed_getters.rs:21:5
+ --> $DIR/misnamed_getters.rs:22:5
|
LL | / fn b(self) -> u8 {
LL | |
@@ -33,7 +33,7 @@ LL | | }
| |_____^
error: getter function appears to return the wrong field
- --> $DIR/misnamed_getters.rs:26:5
+ --> $DIR/misnamed_getters.rs:27:5
|
LL | / fn b_mut(&mut self) -> &mut u8 {
LL | |
@@ -43,7 +43,7 @@ LL | | }
| |_____^
error: getter function appears to return the wrong field
- --> $DIR/misnamed_getters.rs:31:5
+ --> $DIR/misnamed_getters.rs:32:5
|
LL | / fn c(&self) -> &u8 {
LL | |
@@ -53,7 +53,7 @@ LL | | }
| |_____^
error: getter function appears to return the wrong field
- --> $DIR/misnamed_getters.rs:36:5
+ --> $DIR/misnamed_getters.rs:37:5
|
LL | / fn c_mut(&mut self) -> &mut u8 {
LL | |
@@ -63,7 +63,7 @@ LL | | }
| |_____^
error: getter function appears to return the wrong field
- --> $DIR/misnamed_getters.rs:48:5
+ --> $DIR/misnamed_getters.rs:49:5
|
LL | / unsafe fn a(&self) -> &u8 {
LL | |
@@ -73,7 +73,7 @@ LL | | }
| |_____^
error: getter function appears to return the wrong field
- --> $DIR/misnamed_getters.rs:52:5
+ --> $DIR/misnamed_getters.rs:53:5
|
LL | / unsafe fn a_mut(&mut self) -> &mut u8 {
LL | |
@@ -83,7 +83,7 @@ LL | | }
| |_____^
error: getter function appears to return the wrong field
- --> $DIR/misnamed_getters.rs:57:5
+ --> $DIR/misnamed_getters.rs:58:5
|
LL | / unsafe fn b(self) -> u8 {
LL | |
@@ -93,7 +93,7 @@ LL | | }
| |_____^
error: getter function appears to return the wrong field
- --> $DIR/misnamed_getters.rs:62:5
+ --> $DIR/misnamed_getters.rs:63:5
|
LL | / unsafe fn b_mut(&mut self) -> &mut u8 {
LL | |
@@ -103,7 +103,7 @@ LL | | }
| |_____^
error: getter function appears to return the wrong field
- --> $DIR/misnamed_getters.rs:75:5
+ --> $DIR/misnamed_getters.rs:76:5
|
LL | / unsafe fn a_unchecked(&self) -> &u8 {
LL | |
@@ -113,7 +113,7 @@ LL | | }
| |_____^
error: getter function appears to return the wrong field
- --> $DIR/misnamed_getters.rs:79:5
+ --> $DIR/misnamed_getters.rs:80:5
|
LL | / unsafe fn a_unchecked_mut(&mut self) -> &mut u8 {
LL | |
@@ -123,7 +123,7 @@ LL | | }
| |_____^
error: getter function appears to return the wrong field
- --> $DIR/misnamed_getters.rs:84:5
+ --> $DIR/misnamed_getters.rs:85:5
|
LL | / unsafe fn b_unchecked(self) -> u8 {
LL | |
@@ -133,7 +133,7 @@ LL | | }
| |_____^
error: getter function appears to return the wrong field
- --> $DIR/misnamed_getters.rs:89:5
+ --> $DIR/misnamed_getters.rs:90:5
|
LL | / unsafe fn b_unchecked_mut(&mut self) -> &mut u8 {
LL | |
@@ -143,7 +143,7 @@ LL | | }
| |_____^
error: getter function appears to return the wrong field
- --> $DIR/misnamed_getters.rs:122:5
+ --> $DIR/misnamed_getters.rs:123:5
|
LL | / fn a(&self) -> &u8 {
LL | |
@@ -153,7 +153,7 @@ LL | | }
| |_____^
error: getter function appears to return the wrong field
- --> $DIR/misnamed_getters.rs:126:5
+ --> $DIR/misnamed_getters.rs:127:5
|
LL | / fn a_mut(&mut self) -> &mut u8 {
LL | |
@@ -163,7 +163,7 @@ LL | | }
| |_____^
error: getter function appears to return the wrong field
- --> $DIR/misnamed_getters.rs:131:5
+ --> $DIR/misnamed_getters.rs:132:5
|
LL | / fn d(&self) -> &u8 {
LL | |
@@ -173,7 +173,7 @@ LL | | }
| |_____^
error: getter function appears to return the wrong field
- --> $DIR/misnamed_getters.rs:135:5
+ --> $DIR/misnamed_getters.rs:136:5
|
LL | / fn d_mut(&mut self) -> &mut u8 {
LL | |
diff --git a/src/tools/clippy/tests/ui/missing_const_for_fn/auxiliary/helper.rs b/src/tools/clippy/tests/ui/missing_const_for_fn/auxiliary/helper.rs
index 7b9dc76b8..775e07114 100644
--- a/src/tools/clippy/tests/ui/missing_const_for_fn/auxiliary/helper.rs
+++ b/src/tools/clippy/tests/ui/missing_const_for_fn/auxiliary/helper.rs
@@ -1,8 +1,8 @@
// This file provides a const function that is unstably const forever.
#![feature(staged_api)]
-#![stable(feature = "1", since = "1.0.0")]
+#![stable(feature = "clippytest", since = "1.0.0")]
-#[stable(feature = "1", since = "1.0.0")]
+#[stable(feature = "clippytest", since = "1.0.0")]
#[rustc_const_unstable(feature = "foo", issue = "none")]
pub const fn unstably_const_fn() {}
diff --git a/src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.rs b/src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.rs
index 4ef6f0ca9..8afb4df20 100644
--- a/src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.rs
+++ b/src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.rs
@@ -147,4 +147,11 @@ fn _field_fn_ptr(x: unsafe fn()) {
}
}
+// await expands to an unsafe block with several operations, but this is fine.: #11312
+async fn await_desugaring_silent() {
+ async fn helper() {}
+
+ helper().await;
+}
+
fn main() {}
diff --git a/src/tools/clippy/tests/ui/must_use_unit.stderr b/src/tools/clippy/tests/ui/must_use_unit.stderr
index e67d9b5b9..f2ee18585 100644
--- a/src/tools/clippy/tests/ui/must_use_unit.stderr
+++ b/src/tools/clippy/tests/ui/must_use_unit.stderr
@@ -4,7 +4,7 @@ error: this unit-returning function has a `#[must_use]` attribute
LL | #[must_use]
| ----------- help: remove the attribute
LL | pub fn must_use_default() {}
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `-D clippy::must-use-unit` implied by `-D warnings`
= help: to override `-D warnings` add `#[allow(clippy::must_use_unit)]`
@@ -23,7 +23,7 @@ error: this unit-returning function has a `#[must_use]` attribute
LL | #[must_use = "With note"]
| ------------------------- help: remove the attribute
LL | pub fn must_use_with_note() {}
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 3 previous errors
diff --git a/src/tools/clippy/tests/ui/needless_bool/fixable.fixed b/src/tools/clippy/tests/ui/needless_bool/fixable.fixed
index c9ea831f8..3059de8f8 100644
--- a/src/tools/clippy/tests/ui/needless_bool/fixable.fixed
+++ b/src/tools/clippy/tests/ui/needless_bool/fixable.fixed
@@ -7,7 +7,8 @@
clippy::equatable_if_let,
clippy::needless_if,
clippy::needless_return,
- clippy::self_named_constructors
+ clippy::self_named_constructors,
+ clippy::struct_field_names
)]
use std::cell::Cell;
diff --git a/src/tools/clippy/tests/ui/needless_bool/fixable.rs b/src/tools/clippy/tests/ui/needless_bool/fixable.rs
index b83d9c3f2..b2cbe86e2 100644
--- a/src/tools/clippy/tests/ui/needless_bool/fixable.rs
+++ b/src/tools/clippy/tests/ui/needless_bool/fixable.rs
@@ -7,7 +7,8 @@
clippy::equatable_if_let,
clippy::needless_if,
clippy::needless_return,
- clippy::self_named_constructors
+ clippy::self_named_constructors,
+ clippy::struct_field_names
)]
use std::cell::Cell;
diff --git a/src/tools/clippy/tests/ui/needless_bool/fixable.stderr b/src/tools/clippy/tests/ui/needless_bool/fixable.stderr
index 2b189c898..72b0670c9 100644
--- a/src/tools/clippy/tests/ui/needless_bool/fixable.stderr
+++ b/src/tools/clippy/tests/ui/needless_bool/fixable.stderr
@@ -1,5 +1,5 @@
error: this if-then-else expression returns a bool literal
- --> $DIR/fixable.rs:40:5
+ --> $DIR/fixable.rs:41:5
|
LL | / if x {
LL | | true
@@ -12,7 +12,7 @@ LL | | };
= help: to override `-D warnings` add `#[allow(clippy::needless_bool)]`
error: this if-then-else expression returns a bool literal
- --> $DIR/fixable.rs:45:5
+ --> $DIR/fixable.rs:46:5
|
LL | / if x {
LL | | false
@@ -22,7 +22,7 @@ LL | | };
| |_____^ help: you can reduce it to: `!x`
error: this if-then-else expression returns a bool literal
- --> $DIR/fixable.rs:50:5
+ --> $DIR/fixable.rs:51:5
|
LL | / if x && y {
LL | | false
@@ -32,7 +32,7 @@ LL | | };
| |_____^ help: you can reduce it to: `!(x && y)`
error: this if-then-else expression returns a bool literal
- --> $DIR/fixable.rs:58:5
+ --> $DIR/fixable.rs:59:5
|
LL | / if a == b {
LL | | false
@@ -42,7 +42,7 @@ LL | | };
| |_____^ help: you can reduce it to: `a != b`
error: this if-then-else expression returns a bool literal
- --> $DIR/fixable.rs:63:5
+ --> $DIR/fixable.rs:64:5
|
LL | / if a != b {
LL | | false
@@ -52,7 +52,7 @@ LL | | };
| |_____^ help: you can reduce it to: `a == b`
error: this if-then-else expression returns a bool literal
- --> $DIR/fixable.rs:68:5
+ --> $DIR/fixable.rs:69:5
|
LL | / if a < b {
LL | | false
@@ -62,7 +62,7 @@ LL | | };
| |_____^ help: you can reduce it to: `a >= b`
error: this if-then-else expression returns a bool literal
- --> $DIR/fixable.rs:73:5
+ --> $DIR/fixable.rs:74:5
|
LL | / if a <= b {
LL | | false
@@ -72,7 +72,7 @@ LL | | };
| |_____^ help: you can reduce it to: `a > b`
error: this if-then-else expression returns a bool literal
- --> $DIR/fixable.rs:78:5
+ --> $DIR/fixable.rs:79:5
|
LL | / if a > b {
LL | | false
@@ -82,7 +82,7 @@ LL | | };
| |_____^ help: you can reduce it to: `a <= b`
error: this if-then-else expression returns a bool literal
- --> $DIR/fixable.rs:83:5
+ --> $DIR/fixable.rs:84:5
|
LL | / if a >= b {
LL | | false
@@ -92,7 +92,7 @@ LL | | };
| |_____^ help: you can reduce it to: `a < b`
error: this if-then-else expression returns a bool literal
- --> $DIR/fixable.rs:111:5
+ --> $DIR/fixable.rs:112:5
|
LL | / if x {
LL | | return true;
@@ -102,7 +102,7 @@ LL | | };
| |_____^ help: you can reduce it to: `return x`
error: this if-then-else expression returns a bool literal
- --> $DIR/fixable.rs:119:5
+ --> $DIR/fixable.rs:120:5
|
LL | / if x {
LL | | return false;
@@ -112,7 +112,7 @@ LL | | };
| |_____^ help: you can reduce it to: `return !x`
error: this if-then-else expression returns a bool literal
- --> $DIR/fixable.rs:127:5
+ --> $DIR/fixable.rs:128:5
|
LL | / if x && y {
LL | | return true;
@@ -122,7 +122,7 @@ LL | | };
| |_____^ help: you can reduce it to: `return x && y`
error: this if-then-else expression returns a bool literal
- --> $DIR/fixable.rs:135:5
+ --> $DIR/fixable.rs:136:5
|
LL | / if x && y {
LL | | return false;
@@ -132,7 +132,7 @@ LL | | };
| |_____^ help: you can reduce it to: `return !(x && y)`
error: equality checks against true are unnecessary
- --> $DIR/fixable.rs:143:8
+ --> $DIR/fixable.rs:144:8
|
LL | if x == true {};
| ^^^^^^^^^ help: try simplifying it as shown: `x`
@@ -141,25 +141,25 @@ LL | if x == true {};
= help: to override `-D warnings` add `#[allow(clippy::bool_comparison)]`
error: equality checks against false can be replaced by a negation
- --> $DIR/fixable.rs:147:8
+ --> $DIR/fixable.rs:148:8
|
LL | if x == false {};
| ^^^^^^^^^^ help: try simplifying it as shown: `!x`
error: equality checks against true are unnecessary
- --> $DIR/fixable.rs:157:8
+ --> $DIR/fixable.rs:158:8
|
LL | if x == true {};
| ^^^^^^^^^ help: try simplifying it as shown: `x`
error: equality checks against false can be replaced by a negation
- --> $DIR/fixable.rs:158:8
+ --> $DIR/fixable.rs:159:8
|
LL | if x == false {};
| ^^^^^^^^^^ help: try simplifying it as shown: `!x`
error: this if-then-else expression returns a bool literal
- --> $DIR/fixable.rs:167:12
+ --> $DIR/fixable.rs:168:12
|
LL | } else if returns_bool() {
| ____________^
@@ -170,7 +170,7 @@ LL | | };
| |_____^ help: you can reduce it to: `{ !returns_bool() }`
error: this if-then-else expression returns a bool literal
- --> $DIR/fixable.rs:180:5
+ --> $DIR/fixable.rs:181:5
|
LL | / if unsafe { no(4) } & 1 != 0 {
LL | | true
@@ -180,13 +180,13 @@ LL | | };
| |_____^ help: you can reduce it to: `(unsafe { no(4) } & 1 != 0)`
error: this if-then-else expression returns a bool literal
- --> $DIR/fixable.rs:185:30
+ --> $DIR/fixable.rs:186:30
|
LL | let _brackets_unneeded = if unsafe { no(4) } & 1 != 0 { true } else { false };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can reduce it to: `unsafe { no(4) } & 1 != 0`
error: this if-then-else expression returns a bool literal
- --> $DIR/fixable.rs:188:9
+ --> $DIR/fixable.rs:189:9
|
LL | if unsafe { no(4) } & 1 != 0 { true } else { false }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can reduce it to: `(unsafe { no(4) } & 1 != 0)`
diff --git a/src/tools/clippy/tests/ui/needless_if.fixed b/src/tools/clippy/tests/ui/needless_if.fixed
index b84182c57..be35dcddb 100644
--- a/src/tools/clippy/tests/ui/needless_if.fixed
+++ b/src/tools/clippy/tests/ui/needless_if.fixed
@@ -39,11 +39,21 @@ fn main() {
}
// Do not lint `if let` or let chains
if let true = true {}
- if let true = true && true {}
- if true && let true = true {}
+ if let true = true
+ && true
+ {}
+ if true
+ && let true = true
+ {}
// Can lint nested `if let`s
({
- if let true = true && true { true } else { false }
+ if let true = true
+ && true
+ {
+ true
+ } else {
+ false
+ }
} && true);
external! { if (true) {} }
with_span! {
diff --git a/src/tools/clippy/tests/ui/needless_if.rs b/src/tools/clippy/tests/ui/needless_if.rs
index 6c6023c72..e2ad17e69 100644
--- a/src/tools/clippy/tests/ui/needless_if.rs
+++ b/src/tools/clippy/tests/ui/needless_if.rs
@@ -39,11 +39,21 @@ fn main() {
}
// Do not lint `if let` or let chains
if let true = true {}
- if let true = true && true {}
- if true && let true = true {}
+ if let true = true
+ && true
+ {}
+ if true
+ && let true = true
+ {}
// Can lint nested `if let`s
if {
- if let true = true && true { true } else { false }
+ if let true = true
+ && true
+ {
+ true
+ } else {
+ false
+ }
} && true
{}
external! { if (true) {} }
diff --git a/src/tools/clippy/tests/ui/needless_if.stderr b/src/tools/clippy/tests/ui/needless_if.stderr
index ed5b9452b..c3e83c0f1 100644
--- a/src/tools/clippy/tests/ui/needless_if.stderr
+++ b/src/tools/clippy/tests/ui/needless_if.stderr
@@ -29,10 +29,13 @@ LL + });
|
error: this `if` branch is empty
- --> $DIR/needless_if.rs:45:5
+ --> $DIR/needless_if.rs:49:5
|
LL | / if {
-LL | | if let true = true && true { true } else { false }
+LL | | if let true = true
+LL | | && true
+LL | | {
+... |
LL | | } && true
LL | | {}
| |______^
@@ -40,24 +43,30 @@ LL | | {}
help: you can remove it
|
LL ~ ({
-LL + if let true = true && true { true } else { false }
+LL + if let true = true
+LL + && true
+LL + {
+LL + true
+LL + } else {
+LL + false
+LL + }
LL + } && true);
|
error: this `if` branch is empty
- --> $DIR/needless_if.rs:83:5
+ --> $DIR/needless_if.rs:93:5
|
LL | if { maybe_side_effect() } {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can remove it: `({ maybe_side_effect() });`
error: this `if` branch is empty
- --> $DIR/needless_if.rs:85:5
+ --> $DIR/needless_if.rs:95:5
|
LL | if { maybe_side_effect() } && true {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can remove it: `({ maybe_side_effect() } && true);`
error: this `if` branch is empty
- --> $DIR/needless_if.rs:89:5
+ --> $DIR/needless_if.rs:99:5
|
LL | if true {}
| ^^^^^^^^^^ help: you can remove it: `true;`
diff --git a/src/tools/clippy/tests/ui/needless_late_init.fixed b/src/tools/clippy/tests/ui/needless_late_init.fixed
index 9f45da048..891b2b014 100644
--- a/src/tools/clippy/tests/ui/needless_late_init.fixed
+++ b/src/tools/clippy/tests/ui/needless_late_init.fixed
@@ -230,7 +230,9 @@ fn does_not_lint() {
}
let x;
- if true && let Some(n) = Some("let chains too") {
+ if true
+ && let Some(n) = Some("let chains too")
+ {
x = 1;
} else {
x = 2;
diff --git a/src/tools/clippy/tests/ui/needless_late_init.rs b/src/tools/clippy/tests/ui/needless_late_init.rs
index 0dab0faad..553995116 100644
--- a/src/tools/clippy/tests/ui/needless_late_init.rs
+++ b/src/tools/clippy/tests/ui/needless_late_init.rs
@@ -230,7 +230,9 @@ fn does_not_lint() {
}
let x;
- if true && let Some(n) = Some("let chains too") {
+ if true
+ && let Some(n) = Some("let chains too")
+ {
x = 1;
} else {
x = 2;
diff --git a/src/tools/clippy/tests/ui/needless_pass_by_ref_mut.rs b/src/tools/clippy/tests/ui/needless_pass_by_ref_mut.rs
index 39d76f999..bdb6d40d9 100644
--- a/src/tools/clippy/tests/ui/needless_pass_by_ref_mut.rs
+++ b/src/tools/clippy/tests/ui/needless_pass_by_ref_mut.rs
@@ -270,6 +270,51 @@ pub async fn closure4(n: &mut usize) {
})();
}
+// Should not warn.
+async fn _f(v: &mut Vec<()>) {
+ let x = || v.pop();
+ _ = || || x;
+}
+
+struct Data<T: ?Sized> {
+ value: T,
+}
+// Unsafe functions should not warn.
+unsafe fn get_mut_unchecked<T>(ptr: &mut NonNull<Data<T>>) -> &mut T {
+ &mut (*ptr.as_ptr()).value
+}
+// Unsafe blocks should not warn.
+fn get_mut_unchecked2<T>(ptr: &mut NonNull<Data<T>>) -> &mut T {
+ unsafe { &mut (*ptr.as_ptr()).value }
+}
+
+fn set_true(b: &mut bool) {
+ *b = true;
+}
+
+// Should not warn.
+fn true_setter(b: &mut bool) -> impl FnOnce() + '_ {
+ move || set_true(b)
+}
+
+// Should not warn.
+fn filter_copy<T: Copy>(predicate: &mut impl FnMut(T) -> bool) -> impl FnMut(&T) -> bool + '_ {
+ move |&item| predicate(item)
+}
+
+// `is_from_proc_macro` stress tests
+fn _empty_tup(x: &mut (())) {}
+fn _single_tup(x: &mut ((i32,))) {}
+fn _multi_tup(x: &mut ((i32, u32))) {}
+fn _fn(x: &mut (fn())) {}
+#[rustfmt::skip]
+fn _extern_rust_fn(x: &mut extern "Rust" fn()) {}
+fn _extern_c_fn(x: &mut extern "C" fn()) {}
+fn _unsafe_fn(x: &mut unsafe fn()) {}
+fn _unsafe_extern_fn(x: &mut unsafe extern "C" fn()) {}
+fn _fn_with_arg(x: &mut unsafe extern "C" fn(i32)) {}
+fn _fn_with_ret(x: &mut unsafe extern "C" fn() -> (i32)) {}
+
fn main() {
let mut u = 0;
let mut v = vec![0];
diff --git a/src/tools/clippy/tests/ui/needless_pass_by_ref_mut.stderr b/src/tools/clippy/tests/ui/needless_pass_by_ref_mut.stderr
index aa937c3f6..3e1415be0 100644
--- a/src/tools/clippy/tests/ui/needless_pass_by_ref_mut.stderr
+++ b/src/tools/clippy/tests/ui/needless_pass_by_ref_mut.stderr
@@ -139,5 +139,65 @@ LL | pub async fn closure4(n: &mut usize) {
|
= warning: changing this function will impact semver compatibility
-error: aborting due to 21 previous errors
+error: this argument is a mutable reference, but not used mutably
+ --> $DIR/needless_pass_by_ref_mut.rs:306:18
+ |
+LL | fn _empty_tup(x: &mut (())) {}
+ | ^^^^^^^^^ help: consider changing to: `&()`
+
+error: this argument is a mutable reference, but not used mutably
+ --> $DIR/needless_pass_by_ref_mut.rs:307:19
+ |
+LL | fn _single_tup(x: &mut ((i32,))) {}
+ | ^^^^^^^^^^^^^ help: consider changing to: `&(i32,)`
+
+error: this argument is a mutable reference, but not used mutably
+ --> $DIR/needless_pass_by_ref_mut.rs:308:18
+ |
+LL | fn _multi_tup(x: &mut ((i32, u32))) {}
+ | ^^^^^^^^^^^^^^^^^ help: consider changing to: `&(i32, u32)`
+
+error: this argument is a mutable reference, but not used mutably
+ --> $DIR/needless_pass_by_ref_mut.rs:309:11
+ |
+LL | fn _fn(x: &mut (fn())) {}
+ | ^^^^^^^^^^^ help: consider changing to: `&fn()`
+
+error: this argument is a mutable reference, but not used mutably
+ --> $DIR/needless_pass_by_ref_mut.rs:311:23
+ |
+LL | fn _extern_rust_fn(x: &mut extern "Rust" fn()) {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing to: `&extern "Rust" fn()`
+
+error: this argument is a mutable reference, but not used mutably
+ --> $DIR/needless_pass_by_ref_mut.rs:312:20
+ |
+LL | fn _extern_c_fn(x: &mut extern "C" fn()) {}
+ | ^^^^^^^^^^^^^^^^^^^^ help: consider changing to: `&extern "C" fn()`
+
+error: this argument is a mutable reference, but not used mutably
+ --> $DIR/needless_pass_by_ref_mut.rs:313:18
+ |
+LL | fn _unsafe_fn(x: &mut unsafe fn()) {}
+ | ^^^^^^^^^^^^^^^^ help: consider changing to: `&unsafe fn()`
+
+error: this argument is a mutable reference, but not used mutably
+ --> $DIR/needless_pass_by_ref_mut.rs:314:25
+ |
+LL | fn _unsafe_extern_fn(x: &mut unsafe extern "C" fn()) {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing to: `&unsafe extern "C" fn()`
+
+error: this argument is a mutable reference, but not used mutably
+ --> $DIR/needless_pass_by_ref_mut.rs:315:20
+ |
+LL | fn _fn_with_arg(x: &mut unsafe extern "C" fn(i32)) {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing to: `&unsafe extern "C" fn(i32)`
+
+error: this argument is a mutable reference, but not used mutably
+ --> $DIR/needless_pass_by_ref_mut.rs:316:20
+ |
+LL | fn _fn_with_ret(x: &mut unsafe extern "C" fn() -> (i32)) {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing to: `&unsafe extern "C" fn() -> (i32)`
+
+error: aborting due to 31 previous errors
diff --git a/src/tools/clippy/tests/ui/needless_raw_string.fixed b/src/tools/clippy/tests/ui/needless_raw_string.fixed
index 855498105..4ea711fd6 100644
--- a/src/tools/clippy/tests/ui/needless_raw_string.fixed
+++ b/src/tools/clippy/tests/ui/needless_raw_string.fixed
@@ -18,4 +18,8 @@ fn main() {
multiline
string
";
+
+ "no hashes";
+ b"no hashes";
+ c"no hashes";
}
diff --git a/src/tools/clippy/tests/ui/needless_raw_string.rs b/src/tools/clippy/tests/ui/needless_raw_string.rs
index 06d497303..b6239f9b1 100644
--- a/src/tools/clippy/tests/ui/needless_raw_string.rs
+++ b/src/tools/clippy/tests/ui/needless_raw_string.rs
@@ -18,4 +18,8 @@ fn main() {
multiline
string
"#;
+
+ r"no hashes";
+ br"no hashes";
+ cr"no hashes";
}
diff --git a/src/tools/clippy/tests/ui/needless_raw_string.stderr b/src/tools/clippy/tests/ui/needless_raw_string.stderr
index e6806b31b..276bc84c6 100644
--- a/src/tools/clippy/tests/ui/needless_raw_string.stderr
+++ b/src/tools/clippy/tests/ui/needless_raw_string.stderr
@@ -6,7 +6,7 @@ LL | r#"aaa"#;
|
= note: `-D clippy::needless-raw-strings` implied by `-D warnings`
= help: to override `-D warnings` add `#[allow(clippy::needless_raw_strings)]`
-help: try
+help: use a plain string literal instead
|
LL - r#"aaa"#;
LL + "aaa";
@@ -18,7 +18,7 @@ error: unnecessary raw string literal
LL | br#"aaa"#;
| ^^^^^^^^^
|
-help: try
+help: use a plain byte string literal instead
|
LL - br#"aaa"#;
LL + b"aaa";
@@ -30,7 +30,7 @@ error: unnecessary raw string literal
LL | cr#"aaa"#;
| ^^^^^^^^^
|
-help: try
+help: use a plain C string literal instead
|
LL - cr#"aaa"#;
LL + c"aaa";
@@ -46,7 +46,7 @@ LL | | string
LL | | "#;
| |______^
|
-help: try
+help: use a plain string literal instead
|
LL ~ "
LL | a
@@ -55,5 +55,41 @@ LL | string
LL ~ ";
|
-error: aborting due to 4 previous errors
+error: unnecessary raw string literal
+ --> $DIR/needless_raw_string.rs:22:5
+ |
+LL | r"no hashes";
+ | ^^^^^^^^^^^^
+ |
+help: use a plain string literal instead
+ |
+LL - r"no hashes";
+LL + "no hashes";
+ |
+
+error: unnecessary raw string literal
+ --> $DIR/needless_raw_string.rs:23:5
+ |
+LL | br"no hashes";
+ | ^^^^^^^^^^^^^
+ |
+help: use a plain byte string literal instead
+ |
+LL - br"no hashes";
+LL + b"no hashes";
+ |
+
+error: unnecessary raw string literal
+ --> $DIR/needless_raw_string.rs:24:5
+ |
+LL | cr"no hashes";
+ | ^^^^^^^^^^^^^
+ |
+help: use a plain C string literal instead
+ |
+LL - cr"no hashes";
+LL + c"no hashes";
+ |
+
+error: aborting due to 7 previous errors
diff --git a/src/tools/clippy/tests/ui/needless_raw_string_hashes.stderr b/src/tools/clippy/tests/ui/needless_raw_string_hashes.stderr
index 4399c6555..017160b1a 100644
--- a/src/tools/clippy/tests/ui/needless_raw_string_hashes.stderr
+++ b/src/tools/clippy/tests/ui/needless_raw_string_hashes.stderr
@@ -6,7 +6,7 @@ LL | r#"\aaa"#;
|
= note: `-D clippy::needless-raw-string-hashes` implied by `-D warnings`
= help: to override `-D warnings` add `#[allow(clippy::needless_raw_string_hashes)]`
-help: remove all the hashes around the literal
+help: remove all the hashes around the string literal
|
LL - r#"\aaa"#;
LL + r"\aaa";
@@ -18,7 +18,7 @@ error: unnecessary hashes around raw string literal
LL | r##"Hello "world"!"##;
| ^^^^^^^^^^^^^^^^^^^^^
|
-help: remove one hash from both sides of the literal
+help: remove one hash from both sides of the string literal
|
LL - r##"Hello "world"!"##;
LL + r#"Hello "world"!"#;
@@ -30,7 +30,7 @@ error: unnecessary hashes around raw string literal
LL | r######" "### "## "# "######;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
-help: remove 2 hashes from both sides of the literal
+help: remove 2 hashes from both sides of the string literal
|
LL - r######" "### "## "# "######;
LL + r####" "### "## "# "####;
@@ -42,7 +42,7 @@ error: unnecessary hashes around raw string literal
LL | r######" "aa" "# "## "######;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
-help: remove 3 hashes from both sides of the literal
+help: remove 3 hashes from both sides of the string literal
|
LL - r######" "aa" "# "## "######;
LL + r###" "aa" "# "## "###;
@@ -54,7 +54,7 @@ error: unnecessary hashes around raw string literal
LL | br#"\aaa"#;
| ^^^^^^^^^^
|
-help: remove all the hashes around the literal
+help: remove all the hashes around the byte string literal
|
LL - br#"\aaa"#;
LL + br"\aaa";
@@ -66,7 +66,7 @@ error: unnecessary hashes around raw string literal
LL | br##"Hello "world"!"##;
| ^^^^^^^^^^^^^^^^^^^^^^
|
-help: remove one hash from both sides of the literal
+help: remove one hash from both sides of the byte string literal
|
LL - br##"Hello "world"!"##;
LL + br#"Hello "world"!"#;
@@ -78,7 +78,7 @@ error: unnecessary hashes around raw string literal
LL | br######" "### "## "# "######;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
-help: remove 2 hashes from both sides of the literal
+help: remove 2 hashes from both sides of the byte string literal
|
LL - br######" "### "## "# "######;
LL + br####" "### "## "# "####;
@@ -90,7 +90,7 @@ error: unnecessary hashes around raw string literal
LL | br######" "aa" "# "## "######;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
-help: remove 3 hashes from both sides of the literal
+help: remove 3 hashes from both sides of the byte string literal
|
LL - br######" "aa" "# "## "######;
LL + br###" "aa" "# "## "###;
@@ -102,7 +102,7 @@ error: unnecessary hashes around raw string literal
LL | cr#"\aaa"#;
| ^^^^^^^^^^
|
-help: remove all the hashes around the literal
+help: remove all the hashes around the C string literal
|
LL - cr#"\aaa"#;
LL + cr"\aaa";
@@ -114,7 +114,7 @@ error: unnecessary hashes around raw string literal
LL | cr##"Hello "world"!"##;
| ^^^^^^^^^^^^^^^^^^^^^^
|
-help: remove one hash from both sides of the literal
+help: remove one hash from both sides of the C string literal
|
LL - cr##"Hello "world"!"##;
LL + cr#"Hello "world"!"#;
@@ -126,7 +126,7 @@ error: unnecessary hashes around raw string literal
LL | cr######" "### "## "# "######;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
-help: remove 2 hashes from both sides of the literal
+help: remove 2 hashes from both sides of the C string literal
|
LL - cr######" "### "## "# "######;
LL + cr####" "### "## "# "####;
@@ -138,7 +138,7 @@ error: unnecessary hashes around raw string literal
LL | cr######" "aa" "# "## "######;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
-help: remove 3 hashes from both sides of the literal
+help: remove 3 hashes from both sides of the C string literal
|
LL - cr######" "aa" "# "## "######;
LL + cr###" "aa" "# "## "###;
@@ -154,7 +154,7 @@ LL | | string
LL | | "#;
| |______^
|
-help: remove all the hashes around the literal
+help: remove all the hashes around the string literal
|
LL ~ r"
LL | \a
@@ -169,7 +169,7 @@ error: unnecessary hashes around raw string literal
LL | r###"rust"###;
| ^^^^^^^^^^^^^
|
-help: remove all the hashes around the literal
+help: remove all the hashes around the string literal
|
LL - r###"rust"###;
LL + r"rust";
@@ -181,7 +181,7 @@ error: unnecessary hashes around raw string literal
LL | r#"hello world"#;
| ^^^^^^^^^^^^^^^^
|
-help: remove all the hashes around the literal
+help: remove all the hashes around the string literal
|
LL - r#"hello world"#;
LL + r"hello world";
diff --git a/src/tools/clippy/tests/ui/option_if_let_else.fixed b/src/tools/clippy/tests/ui/option_if_let_else.fixed
index c3415a7df..f0113ca69 100644
--- a/src/tools/clippy/tests/ui/option_if_let_else.fixed
+++ b/src/tools/clippy/tests/ui/option_if_let_else.fixed
@@ -1,7 +1,6 @@
#![warn(clippy::option_if_let_else)]
#![allow(
unused_tuple_struct_fields,
- clippy::redundant_closure,
clippy::ref_option_ref,
clippy::equatable_if_let,
clippy::let_unit_value,
@@ -52,7 +51,7 @@ fn impure_else(arg: Option<i32>) {
println!("return 1");
1
};
- let _ = arg.map_or_else(|| side_effect(), |x| x);
+ let _ = arg.map_or_else(side_effect, |x| x);
}
fn test_map_or_else(arg: Option<u32>) {
@@ -224,3 +223,17 @@ mod issue10729 {
fn do_something(_value: &str) {}
fn do_something2(_value: &mut str) {}
}
+
+fn issue11429() {
+ use std::collections::HashMap;
+
+ macro_rules! new_map {
+ () => {{ HashMap::new() }};
+ }
+
+ let opt: Option<HashMap<u8, u8>> = None;
+
+ let mut _hashmap = opt.as_ref().map_or_else(HashMap::new, |hm| hm.clone());
+
+ let mut _hm = opt.as_ref().map_or_else(|| new_map!(), |hm| hm.clone());
+}
diff --git a/src/tools/clippy/tests/ui/option_if_let_else.rs b/src/tools/clippy/tests/ui/option_if_let_else.rs
index 86537f620..18b7af443 100644
--- a/src/tools/clippy/tests/ui/option_if_let_else.rs
+++ b/src/tools/clippy/tests/ui/option_if_let_else.rs
@@ -1,7 +1,6 @@
#![warn(clippy::option_if_let_else)]
#![allow(
unused_tuple_struct_fields,
- clippy::redundant_closure,
clippy::ref_option_ref,
clippy::equatable_if_let,
clippy::let_unit_value,
@@ -271,3 +270,21 @@ mod issue10729 {
fn do_something(_value: &str) {}
fn do_something2(_value: &mut str) {}
}
+
+fn issue11429() {
+ use std::collections::HashMap;
+
+ macro_rules! new_map {
+ () => {{ HashMap::new() }};
+ }
+
+ let opt: Option<HashMap<u8, u8>> = None;
+
+ let mut _hashmap = if let Some(hm) = &opt {
+ hm.clone()
+ } else {
+ HashMap::new()
+ };
+
+ let mut _hm = if let Some(hm) = &opt { hm.clone() } else { new_map!() };
+}
diff --git a/src/tools/clippy/tests/ui/option_if_let_else.stderr b/src/tools/clippy/tests/ui/option_if_let_else.stderr
index 6d7d02f8c..e36357bcb 100644
--- a/src/tools/clippy/tests/ui/option_if_let_else.stderr
+++ b/src/tools/clippy/tests/ui/option_if_let_else.stderr
@@ -1,5 +1,5 @@
error: use Option::map_or instead of an if let/else
- --> $DIR/option_if_let_else.rs:12:5
+ --> $DIR/option_if_let_else.rs:11:5
|
LL | / if let Some(x) = string {
LL | | (true, x)
@@ -12,19 +12,19 @@ LL | | }
= help: to override `-D warnings` add `#[allow(clippy::option_if_let_else)]`
error: use Option::map_or instead of an if let/else
- --> $DIR/option_if_let_else.rs:30:13
+ --> $DIR/option_if_let_else.rs:29:13
|
LL | let _ = if let Some(s) = *string { s.len() } else { 0 };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `string.map_or(0, |s| s.len())`
error: use Option::map_or instead of an if let/else
- --> $DIR/option_if_let_else.rs:31:13
+ --> $DIR/option_if_let_else.rs:30:13
|
LL | let _ = if let Some(s) = &num { s } else { &0 };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `num.as_ref().map_or(&0, |s| s)`
error: use Option::map_or instead of an if let/else
- --> $DIR/option_if_let_else.rs:32:13
+ --> $DIR/option_if_let_else.rs:31:13
|
LL | let _ = if let Some(s) = &mut num {
| _____________^
@@ -44,13 +44,13 @@ LL ~ });
|
error: use Option::map_or instead of an if let/else
- --> $DIR/option_if_let_else.rs:38:13
+ --> $DIR/option_if_let_else.rs:37:13
|
LL | let _ = if let Some(ref s) = num { s } else { &0 };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `num.as_ref().map_or(&0, |s| s)`
error: use Option::map_or instead of an if let/else
- --> $DIR/option_if_let_else.rs:39:13
+ --> $DIR/option_if_let_else.rs:38:13
|
LL | let _ = if let Some(mut s) = num {
| _____________^
@@ -70,7 +70,7 @@ LL ~ });
|
error: use Option::map_or instead of an if let/else
- --> $DIR/option_if_let_else.rs:45:13
+ --> $DIR/option_if_let_else.rs:44:13
|
LL | let _ = if let Some(ref mut s) = num {
| _____________^
@@ -90,7 +90,7 @@ LL ~ });
|
error: use Option::map_or instead of an if let/else
- --> $DIR/option_if_let_else.rs:54:5
+ --> $DIR/option_if_let_else.rs:53:5
|
LL | / if let Some(x) = arg {
LL | | let y = x * x;
@@ -109,7 +109,7 @@ LL + })
|
error: use Option::map_or_else instead of an if let/else
- --> $DIR/option_if_let_else.rs:67:13
+ --> $DIR/option_if_let_else.rs:66:13
|
LL | let _ = if let Some(x) = arg {
| _____________^
@@ -118,10 +118,10 @@ LL | | } else {
LL | | // map_or_else must be suggested
LL | | side_effect()
LL | | };
- | |_____^ help: try: `arg.map_or_else(|| side_effect(), |x| x)`
+ | |_____^ help: try: `arg.map_or_else(side_effect, |x| x)`
error: use Option::map_or_else instead of an if let/else
- --> $DIR/option_if_let_else.rs:76:13
+ --> $DIR/option_if_let_else.rs:75:13
|
LL | let _ = if let Some(x) = arg {
| _____________^
@@ -144,7 +144,7 @@ LL ~ }, |x| x * x * x * x);
|
error: use Option::map_or_else instead of an if let/else
- --> $DIR/option_if_let_else.rs:109:13
+ --> $DIR/option_if_let_else.rs:108:13
|
LL | / if let Some(idx) = s.find('.') {
LL | | vec![s[..idx].to_string(), s[idx..].to_string()]
@@ -154,7 +154,7 @@ LL | | }
| |_____________^ help: try: `s.find('.').map_or_else(|| vec![s.to_string()], |idx| vec![s[..idx].to_string(), s[idx..].to_string()])`
error: use Option::map_or_else instead of an if let/else
- --> $DIR/option_if_let_else.rs:120:5
+ --> $DIR/option_if_let_else.rs:119:5
|
LL | / if let Ok(binding) = variable {
LL | | println!("Ok {binding}");
@@ -173,13 +173,13 @@ LL + })
|
error: use Option::map_or instead of an if let/else
- --> $DIR/option_if_let_else.rs:142:13
+ --> $DIR/option_if_let_else.rs:141:13
|
LL | let _ = if let Some(x) = optional { x + 2 } else { 5 };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `optional.map_or(5, |x| x + 2)`
error: use Option::map_or instead of an if let/else
- --> $DIR/option_if_let_else.rs:152:13
+ --> $DIR/option_if_let_else.rs:151:13
|
LL | let _ = if let Some(x) = Some(0) {
| _____________^
@@ -201,13 +201,13 @@ LL ~ });
|
error: use Option::map_or instead of an if let/else
- --> $DIR/option_if_let_else.rs:180:13
+ --> $DIR/option_if_let_else.rs:179:13
|
LL | let _ = if let Some(x) = Some(0) { s.len() + x } else { s.len() };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Some(0).map_or(s.len(), |x| s.len() + x)`
error: use Option::map_or instead of an if let/else
- --> $DIR/option_if_let_else.rs:184:13
+ --> $DIR/option_if_let_else.rs:183:13
|
LL | let _ = if let Some(x) = Some(0) {
| _____________^
@@ -227,7 +227,7 @@ LL ~ });
|
error: use Option::map_or instead of an if let/else
- --> $DIR/option_if_let_else.rs:223:13
+ --> $DIR/option_if_let_else.rs:222:13
|
LL | let _ = match s {
| _____________^
@@ -237,7 +237,7 @@ LL | | };
| |_____^ help: try: `s.map_or(1, |string| string.len())`
error: use Option::map_or instead of an if let/else
- --> $DIR/option_if_let_else.rs:227:13
+ --> $DIR/option_if_let_else.rs:226:13
|
LL | let _ = match Some(10) {
| _____________^
@@ -247,7 +247,7 @@ LL | | };
| |_____^ help: try: `Some(10).map_or(5, |a| a + 1)`
error: use Option::map_or instead of an if let/else
- --> $DIR/option_if_let_else.rs:233:13
+ --> $DIR/option_if_let_else.rs:232:13
|
LL | let _ = match res {
| _____________^
@@ -257,7 +257,7 @@ LL | | };
| |_____^ help: try: `res.map_or(1, |a| a + 1)`
error: use Option::map_or instead of an if let/else
- --> $DIR/option_if_let_else.rs:237:13
+ --> $DIR/option_if_let_else.rs:236:13
|
LL | let _ = match res {
| _____________^
@@ -267,13 +267,13 @@ LL | | };
| |_____^ help: try: `res.map_or(1, |a| a + 1)`
error: use Option::map_or instead of an if let/else
- --> $DIR/option_if_let_else.rs:241:13
+ --> $DIR/option_if_let_else.rs:240:13
|
LL | let _ = if let Ok(a) = res { a + 1 } else { 5 };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `res.map_or(5, |a| a + 1)`
error: use Option::map_or instead of an if let/else
- --> $DIR/option_if_let_else.rs:258:9
+ --> $DIR/option_if_let_else.rs:257:9
|
LL | / match initial {
LL | | Some(value) => do_something(value),
@@ -282,7 +282,7 @@ LL | | }
| |_________^ help: try: `initial.as_ref().map_or({}, |value| do_something(value))`
error: use Option::map_or instead of an if let/else
- --> $DIR/option_if_let_else.rs:265:9
+ --> $DIR/option_if_let_else.rs:264:9
|
LL | / match initial {
LL | | Some(value) => do_something2(value),
@@ -290,5 +290,22 @@ LL | | None => {},
LL | | }
| |_________^ help: try: `initial.as_mut().map_or({}, |value| do_something2(value))`
-error: aborting due to 23 previous errors
+error: use Option::map_or_else instead of an if let/else
+ --> $DIR/option_if_let_else.rs:283:24
+ |
+LL | let mut _hashmap = if let Some(hm) = &opt {
+ | ________________________^
+LL | | hm.clone()
+LL | | } else {
+LL | | HashMap::new()
+LL | | };
+ | |_____^ help: try: `opt.as_ref().map_or_else(HashMap::new, |hm| hm.clone())`
+
+error: use Option::map_or_else instead of an if let/else
+ --> $DIR/option_if_let_else.rs:289:19
+ |
+LL | let mut _hm = if let Some(hm) = &opt { hm.clone() } else { new_map!() };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `opt.as_ref().map_or_else(|| new_map!(), |hm| hm.clone())`
+
+error: aborting due to 25 previous errors
diff --git a/src/tools/clippy/tests/ui/print_literal.fixed b/src/tools/clippy/tests/ui/print_literal.fixed
index 88cd3a54b..a7157c07f 100644
--- a/src/tools/clippy/tests/ui/print_literal.fixed
+++ b/src/tools/clippy/tests/ui/print_literal.fixed
@@ -39,20 +39,30 @@ fn main() {
// throw a warning
println!("hello world");
//~^ ERROR: literal with an empty format string
- //~| ERROR: literal with an empty format string
println!("world hello");
//~^ ERROR: literal with an empty format string
- //~| ERROR: literal with an empty format string
// named args shouldn't change anything either
println!("hello world");
//~^ ERROR: literal with an empty format string
- //~| ERROR: literal with an empty format string
println!("world hello");
//~^ ERROR: literal with an empty format string
- //~| ERROR: literal with an empty format string
// The string literal from `file!()` has a callsite span that isn't marked as coming from an
// expansion
println!("file: {}", file!());
+
+ // Braces in unicode escapes should not be escaped
+ println!("{{}} \x00 \u{ab123} \\\u{ab123} {{:?}}");
+ println!("\\\u{1234}");
+ // This does not lint because it would have to suggest unescaping the character
+ println!(r"{}", "\u{ab123}");
+ // These are not unicode escapes
+ println!("\\u{{ab123}} \\u{{{{");
+ println!(r"\u{{ab123}} \u{{{{");
+ println!("\\{{ab123}} \\u{{{{");
+ println!("\\u{{ab123}}");
+ println!("\\\\u{{1234}}");
+
+ println!("mixed: {{hello}} {world}");
}
diff --git a/src/tools/clippy/tests/ui/print_literal.rs b/src/tools/clippy/tests/ui/print_literal.rs
index bd7444c96..4b04b4274 100644
--- a/src/tools/clippy/tests/ui/print_literal.rs
+++ b/src/tools/clippy/tests/ui/print_literal.rs
@@ -39,20 +39,30 @@ fn main() {
// throw a warning
println!("{0} {1}", "hello", "world");
//~^ ERROR: literal with an empty format string
- //~| ERROR: literal with an empty format string
println!("{1} {0}", "hello", "world");
//~^ ERROR: literal with an empty format string
- //~| ERROR: literal with an empty format string
// named args shouldn't change anything either
println!("{foo} {bar}", foo = "hello", bar = "world");
//~^ ERROR: literal with an empty format string
- //~| ERROR: literal with an empty format string
println!("{bar} {foo}", foo = "hello", bar = "world");
//~^ ERROR: literal with an empty format string
- //~| ERROR: literal with an empty format string
// The string literal from `file!()` has a callsite span that isn't marked as coming from an
// expansion
println!("file: {}", file!());
+
+ // Braces in unicode escapes should not be escaped
+ println!("{}", "{} \x00 \u{ab123} \\\u{ab123} {:?}");
+ println!("{}", "\\\u{1234}");
+ // This does not lint because it would have to suggest unescaping the character
+ println!(r"{}", "\u{ab123}");
+ // These are not unicode escapes
+ println!("{}", r"\u{ab123} \u{{");
+ println!(r"{}", r"\u{ab123} \u{{");
+ println!("{}", r"\{ab123} \u{{");
+ println!("{}", "\\u{ab123}");
+ println!("{}", "\\\\u{1234}");
+
+ println!("mixed: {} {world}", "{hello}");
}
diff --git a/src/tools/clippy/tests/ui/print_literal.stderr b/src/tools/clippy/tests/ui/print_literal.stderr
index 1d9751b92..8c011d7bc 100644
--- a/src/tools/clippy/tests/ui/print_literal.stderr
+++ b/src/tools/clippy/tests/ui/print_literal.stderr
@@ -52,97 +52,145 @@ error: literal with an empty format string
--> $DIR/print_literal.rs:40:25
|
LL | println!("{0} {1}", "hello", "world");
- | ^^^^^^^
+ | ^^^^^^^^^^^^^^^^
|
help: try
|
LL - println!("{0} {1}", "hello", "world");
-LL + println!("hello {1}", "world");
+LL + println!("hello world");
|
error: literal with an empty format string
- --> $DIR/print_literal.rs:40:34
+ --> $DIR/print_literal.rs:42:25
|
-LL | println!("{0} {1}", "hello", "world");
- | ^^^^^^^
+LL | println!("{1} {0}", "hello", "world");
+ | ^^^^^^^^^^^^^^^^
|
help: try
|
-LL - println!("{0} {1}", "hello", "world");
-LL + println!("{0} world", "hello");
+LL - println!("{1} {0}", "hello", "world");
+LL + println!("world hello");
|
error: literal with an empty format string
- --> $DIR/print_literal.rs:43:34
+ --> $DIR/print_literal.rs:46:35
|
-LL | println!("{1} {0}", "hello", "world");
- | ^^^^^^^
+LL | println!("{foo} {bar}", foo = "hello", bar = "world");
+ | ^^^^^^^^^^^^^^^^^^^^^^
|
help: try
|
-LL - println!("{1} {0}", "hello", "world");
-LL + println!("world {0}", "hello");
+LL - println!("{foo} {bar}", foo = "hello", bar = "world");
+LL + println!("hello world");
|
error: literal with an empty format string
- --> $DIR/print_literal.rs:43:25
+ --> $DIR/print_literal.rs:48:35
|
-LL | println!("{1} {0}", "hello", "world");
- | ^^^^^^^
+LL | println!("{bar} {foo}", foo = "hello", bar = "world");
+ | ^^^^^^^^^^^^^^^^^^^^^^
|
help: try
|
-LL - println!("{1} {0}", "hello", "world");
-LL + println!("{1} hello", "world");
+LL - println!("{bar} {foo}", foo = "hello", bar = "world");
+LL + println!("world hello");
|
error: literal with an empty format string
- --> $DIR/print_literal.rs:48:35
+ --> $DIR/print_literal.rs:56:20
|
-LL | println!("{foo} {bar}", foo = "hello", bar = "world");
- | ^^^^^^^
+LL | println!("{}", "{} \x00 \u{ab123} \\\u{ab123} {:?}");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: try
|
-LL - println!("{foo} {bar}", foo = "hello", bar = "world");
-LL + println!("hello {bar}", bar = "world");
+LL - println!("{}", "{} \x00 \u{ab123} \\\u{ab123} {:?}");
+LL + println!("{{}} \x00 \u{ab123} \\\u{ab123} {{:?}}");
|
error: literal with an empty format string
- --> $DIR/print_literal.rs:48:50
+ --> $DIR/print_literal.rs:57:20
|
-LL | println!("{foo} {bar}", foo = "hello", bar = "world");
- | ^^^^^^^
+LL | println!("{}", "\\\u{1234}");
+ | ^^^^^^^^^^^^
|
help: try
|
-LL - println!("{foo} {bar}", foo = "hello", bar = "world");
-LL + println!("{foo} world", foo = "hello");
+LL - println!("{}", "\\\u{1234}");
+LL + println!("\\\u{1234}");
|
error: literal with an empty format string
- --> $DIR/print_literal.rs:51:50
+ --> $DIR/print_literal.rs:61:20
|
-LL | println!("{bar} {foo}", foo = "hello", bar = "world");
- | ^^^^^^^
+LL | println!("{}", r"\u{ab123} \u{{");
+ | ^^^^^^^^^^^^^^^^^
|
help: try
|
-LL - println!("{bar} {foo}", foo = "hello", bar = "world");
-LL + println!("world {foo}", foo = "hello");
+LL - println!("{}", r"\u{ab123} \u{{");
+LL + println!("\\u{{ab123}} \\u{{{{");
|
error: literal with an empty format string
- --> $DIR/print_literal.rs:51:35
+ --> $DIR/print_literal.rs:62:21
|
-LL | println!("{bar} {foo}", foo = "hello", bar = "world");
- | ^^^^^^^
+LL | println!(r"{}", r"\u{ab123} \u{{");
+ | ^^^^^^^^^^^^^^^^^
|
help: try
|
-LL - println!("{bar} {foo}", foo = "hello", bar = "world");
-LL + println!("{bar} hello", bar = "world");
+LL - println!(r"{}", r"\u{ab123} \u{{");
+LL + println!(r"\u{{ab123}} \u{{{{");
+ |
+
+error: literal with an empty format string
+ --> $DIR/print_literal.rs:63:20
+ |
+LL | println!("{}", r"\{ab123} \u{{");
+ | ^^^^^^^^^^^^^^^^
+ |
+help: try
+ |
+LL - println!("{}", r"\{ab123} \u{{");
+LL + println!("\\{{ab123}} \\u{{{{");
+ |
+
+error: literal with an empty format string
+ --> $DIR/print_literal.rs:64:20
+ |
+LL | println!("{}", "\\u{ab123}");
+ | ^^^^^^^^^^^^
+ |
+help: try
+ |
+LL - println!("{}", "\\u{ab123}");
+LL + println!("\\u{{ab123}}");
+ |
+
+error: literal with an empty format string
+ --> $DIR/print_literal.rs:65:20
+ |
+LL | println!("{}", "\\\\u{1234}");
+ | ^^^^^^^^^^^^^
+ |
+help: try
+ |
+LL - println!("{}", "\\\\u{1234}");
+LL + println!("\\\\u{{1234}}");
+ |
+
+error: literal with an empty format string
+ --> $DIR/print_literal.rs:67:35
+ |
+LL | println!("mixed: {} {world}", "{hello}");
+ | ^^^^^^^^^
+ |
+help: try
+ |
+LL - println!("mixed: {} {world}", "{hello}");
+LL + println!("mixed: {{hello}} {world}");
|
-error: aborting due to 12 previous errors
+error: aborting due to 16 previous errors
diff --git a/src/tools/clippy/tests/ui/redundant_guards.fixed b/src/tools/clippy/tests/ui/redundant_guards.fixed
index f23116a7e..f8af90927 100644
--- a/src/tools/clippy/tests/ui/redundant_guards.fixed
+++ b/src/tools/clippy/tests/ui/redundant_guards.fixed
@@ -48,7 +48,7 @@ fn main() {
Some(x) if let Some(1) = x => {
x;
..
- }
+ },
_ => todo!(),
};
let y = 1;
diff --git a/src/tools/clippy/tests/ui/redundant_guards.rs b/src/tools/clippy/tests/ui/redundant_guards.rs
index c0206b4ce..b46f8a620 100644
--- a/src/tools/clippy/tests/ui/redundant_guards.rs
+++ b/src/tools/clippy/tests/ui/redundant_guards.rs
@@ -48,7 +48,7 @@ fn main() {
Some(x) if let Some(1) = x => {
x;
..
- }
+ },
_ => todo!(),
};
let y = 1;
diff --git a/src/tools/clippy/tests/ui/redundant_locals.rs b/src/tools/clippy/tests/ui/redundant_locals.rs
index c5d93e436..182d067a5 100644
--- a/src/tools/clippy/tests/ui/redundant_locals.rs
+++ b/src/tools/clippy/tests/ui/redundant_locals.rs
@@ -117,4 +117,49 @@ fn macros() {
let x = 1;
let x = x;
}
+
+ let x = 10;
+ macro_rules! rebind_outer_macro {
+ ($x:ident) => {
+ let x = x;
+ };
+ }
+ rebind_outer_macro!(y);
+}
+
+struct WithDrop(usize);
+impl Drop for WithDrop {
+ fn drop(&mut self) {}
+}
+
+struct InnerDrop(WithDrop);
+
+struct ComposeDrop {
+ d: WithDrop,
+}
+
+struct WithoutDrop(usize);
+
+fn drop_trait() {
+ let a = WithDrop(1);
+ let b = WithDrop(2);
+ let a = a;
+}
+
+fn without_drop() {
+ let a = WithoutDrop(1);
+ let b = WithoutDrop(2);
+ let a = a;
+}
+
+fn drop_inner() {
+ let a = InnerDrop(WithDrop(1));
+ let b = InnerDrop(WithDrop(2));
+ let a = a;
+}
+
+fn drop_compose() {
+ let a = ComposeDrop { d: WithDrop(1) };
+ let b = ComposeDrop { d: WithDrop(1) };
+ let a = a;
}
diff --git a/src/tools/clippy/tests/ui/redundant_locals.stderr b/src/tools/clippy/tests/ui/redundant_locals.stderr
index 13b872e95..30ab4aa2e 100644
--- a/src/tools/clippy/tests/ui/redundant_locals.stderr
+++ b/src/tools/clippy/tests/ui/redundant_locals.stderr
@@ -1,137 +1,172 @@
-error: redundant redefinition of a binding
- --> $DIR/redundant_locals.rs:11:9
+error: redundant redefinition of a binding `x`
+ --> $DIR/redundant_locals.rs:12:5
|
-LL | let x = 1;
- | ^
LL | let x = x;
| ^^^^^^^^^^
|
- = help: remove the redefinition of `x`
+help: `x` is initially defined here
+ --> $DIR/redundant_locals.rs:11:9
+ |
+LL | let x = 1;
+ | ^
= note: `-D clippy::redundant-locals` implied by `-D warnings`
= help: to override `-D warnings` add `#[allow(clippy::redundant_locals)]`
-error: redundant redefinition of a binding
- --> $DIR/redundant_locals.rs:16:9
+error: redundant redefinition of a binding `x`
+ --> $DIR/redundant_locals.rs:17:5
|
-LL | let mut x = 1;
- | ^^^^^
LL | let mut x = x;
| ^^^^^^^^^^^^^^
|
- = help: remove the redefinition of `x`
+help: `x` is initially defined here
+ --> $DIR/redundant_locals.rs:16:9
+ |
+LL | let mut x = 1;
+ | ^^^^^
-error: redundant redefinition of a binding
+error: redundant redefinition of a binding `x`
+ --> $DIR/redundant_locals.rs:47:5
+ |
+LL | let x = x;
+ | ^^^^^^^^^^
+ |
+help: `x` is initially defined here
--> $DIR/redundant_locals.rs:46:14
|
LL | fn parameter(x: i32) {
| ^
+
+error: redundant redefinition of a binding `x`
+ --> $DIR/redundant_locals.rs:52:5
+ |
LL | let x = x;
| ^^^^^^^^^^
|
- = help: remove the redefinition of `x`
-
-error: redundant redefinition of a binding
+help: `x` is initially defined here
--> $DIR/redundant_locals.rs:51:9
|
LL | let x = 1;
| ^
+
+error: redundant redefinition of a binding `x`
+ --> $DIR/redundant_locals.rs:53:5
+ |
LL | let x = x;
| ^^^^^^^^^^
|
- = help: remove the redefinition of `x`
-
-error: redundant redefinition of a binding
+help: `x` is initially defined here
--> $DIR/redundant_locals.rs:52:9
|
LL | let x = x;
| ^
+
+error: redundant redefinition of a binding `x`
+ --> $DIR/redundant_locals.rs:54:5
+ |
LL | let x = x;
| ^^^^^^^^^^
|
- = help: remove the redefinition of `x`
-
-error: redundant redefinition of a binding
+help: `x` is initially defined here
--> $DIR/redundant_locals.rs:53:9
|
LL | let x = x;
| ^
+
+error: redundant redefinition of a binding `x`
+ --> $DIR/redundant_locals.rs:55:5
+ |
LL | let x = x;
| ^^^^^^^^^^
|
- = help: remove the redefinition of `x`
-
-error: redundant redefinition of a binding
+help: `x` is initially defined here
--> $DIR/redundant_locals.rs:54:9
|
LL | let x = x;
| ^
-LL | let x = x;
+
+error: redundant redefinition of a binding `a`
+ --> $DIR/redundant_locals.rs:61:5
+ |
+LL | let a = a;
| ^^^^^^^^^^
|
- = help: remove the redefinition of `x`
-
-error: redundant redefinition of a binding
+help: `a` is initially defined here
--> $DIR/redundant_locals.rs:59:9
|
LL | let a = 1;
| ^
-LL | let b = 2;
-LL | let a = a;
+
+error: redundant redefinition of a binding `b`
+ --> $DIR/redundant_locals.rs:62:5
+ |
+LL | let b = b;
| ^^^^^^^^^^
|
- = help: remove the redefinition of `a`
-
-error: redundant redefinition of a binding
+help: `b` is initially defined here
--> $DIR/redundant_locals.rs:60:9
|
LL | let b = 2;
| ^
-LL | let a = a;
-LL | let b = b;
- | ^^^^^^^^^^
- |
- = help: remove the redefinition of `b`
-error: redundant redefinition of a binding
+error: redundant redefinition of a binding `x`
+ --> $DIR/redundant_locals.rs:68:9
+ |
+LL | let x = x;
+ | ^^^^^^^^^^
+ |
+help: `x` is initially defined here
--> $DIR/redundant_locals.rs:67:13
|
LL | let x = 1;
| ^
+
+error: redundant redefinition of a binding `x`
+ --> $DIR/redundant_locals.rs:75:9
+ |
LL | let x = x;
| ^^^^^^^^^^
|
- = help: remove the redefinition of `x`
-
-error: redundant redefinition of a binding
+help: `x` is initially defined here
--> $DIR/redundant_locals.rs:74:13
|
LL | let x = 1;
| ^
+
+error: redundant redefinition of a binding `x`
+ --> $DIR/redundant_locals.rs:78:9
+ |
LL | let x = x;
| ^^^^^^^^^^
|
- = help: remove the redefinition of `x`
-
-error: redundant redefinition of a binding
+help: `x` is initially defined here
--> $DIR/redundant_locals.rs:77:6
|
LL | |x: i32| {
| ^
+
+error: redundant redefinition of a binding `x`
+ --> $DIR/redundant_locals.rs:97:9
+ |
LL | let x = x;
| ^^^^^^^^^^
|
- = help: remove the redefinition of `x`
-
-error: redundant redefinition of a binding
+help: `x` is initially defined here
--> $DIR/redundant_locals.rs:94:9
|
LL | let x = 1;
| ^
-...
-LL | let x = x;
- | ^^^^^^^^^^
+
+error: redundant redefinition of a binding `a`
+ --> $DIR/redundant_locals.rs:152:5
+ |
+LL | let a = a;
+ | ^^^^^^^^^^
|
- = help: remove the redefinition of `x`
+help: `a` is initially defined here
+ --> $DIR/redundant_locals.rs:150:9
+ |
+LL | let a = WithoutDrop(1);
+ | ^
-error: aborting due to 13 previous errors
+error: aborting due to 14 previous errors
diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_option.fixed b/src/tools/clippy/tests/ui/redundant_pattern_matching_option.fixed
index 60f9fb6d4..c9b76262d 100644
--- a/src/tools/clippy/tests/ui/redundant_pattern_matching_option.fixed
+++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_option.fixed
@@ -16,10 +16,15 @@ fn issue_11174<T>(boolean: bool, maybe_some: Option<T>) -> bool {
fn issue_11174_edge_cases<T>(boolean: bool, boolean2: bool, maybe_some: Option<T>) {
let _ = maybe_some.is_none() && (boolean || boolean2); // guard needs parentheses
- let _ = match maybe_some { // can't use `matches!` here
- // because `expr` metavars in macros don't allow let exprs
- None if let Some(x) = Some(0) && x > 5 => true,
- _ => false
+ let _ = match maybe_some {
+ // can't use `matches!` here
+ // because `expr` metavars in macros don't allow let exprs
+ None if let Some(x) = Some(0)
+ && x > 5 =>
+ {
+ true
+ },
+ _ => false,
};
}
diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_option.rs b/src/tools/clippy/tests/ui/redundant_pattern_matching_option.rs
index 94bbb569c..a5f9caf65 100644
--- a/src/tools/clippy/tests/ui/redundant_pattern_matching_option.rs
+++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_option.rs
@@ -16,10 +16,15 @@ fn issue_11174<T>(boolean: bool, maybe_some: Option<T>) -> bool {
fn issue_11174_edge_cases<T>(boolean: bool, boolean2: bool, maybe_some: Option<T>) {
let _ = matches!(maybe_some, None if boolean || boolean2); // guard needs parentheses
- let _ = match maybe_some { // can't use `matches!` here
- // because `expr` metavars in macros don't allow let exprs
- None if let Some(x) = Some(0) && x > 5 => true,
- _ => false
+ let _ = match maybe_some {
+ // can't use `matches!` here
+ // because `expr` metavars in macros don't allow let exprs
+ None if let Some(x) = Some(0)
+ && x > 5 =>
+ {
+ true
+ },
+ _ => false,
};
}
diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_option.stderr b/src/tools/clippy/tests/ui/redundant_pattern_matching_option.stderr
index fdf395d82..a75551c56 100644
--- a/src/tools/clippy/tests/ui/redundant_pattern_matching_option.stderr
+++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_option.stderr
@@ -14,49 +14,49 @@ LL | let _ = matches!(maybe_some, None if boolean || boolean2); // guard nee
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `maybe_some.is_none() && (boolean || boolean2)`
error: redundant pattern matching, consider using `is_none()`
- --> $DIR/redundant_pattern_matching_option.rs:27:12
+ --> $DIR/redundant_pattern_matching_option.rs:32:12
|
LL | if let None = None::<()> {}
| -------^^^^------------- help: try: `if None::<()>.is_none()`
error: redundant pattern matching, consider using `is_some()`
- --> $DIR/redundant_pattern_matching_option.rs:29:12
+ --> $DIR/redundant_pattern_matching_option.rs:34:12
|
LL | if let Some(_) = Some(42) {}
| -------^^^^^^^----------- help: try: `if Some(42).is_some()`
error: redundant pattern matching, consider using `is_some()`
- --> $DIR/redundant_pattern_matching_option.rs:31:12
+ --> $DIR/redundant_pattern_matching_option.rs:36:12
|
LL | if let Some(_) = Some(42) {
| -------^^^^^^^----------- help: try: `if Some(42).is_some()`
error: redundant pattern matching, consider using `is_some()`
- --> $DIR/redundant_pattern_matching_option.rs:37:15
+ --> $DIR/redundant_pattern_matching_option.rs:42:15
|
LL | while let Some(_) = Some(42) {}
| ----------^^^^^^^----------- help: try: `while Some(42).is_some()`
error: redundant pattern matching, consider using `is_none()`
- --> $DIR/redundant_pattern_matching_option.rs:39:15
+ --> $DIR/redundant_pattern_matching_option.rs:44:15
|
LL | while let None = Some(42) {}
| ----------^^^^----------- help: try: `while Some(42).is_none()`
error: redundant pattern matching, consider using `is_none()`
- --> $DIR/redundant_pattern_matching_option.rs:41:15
+ --> $DIR/redundant_pattern_matching_option.rs:46:15
|
LL | while let None = None::<()> {}
| ----------^^^^------------- help: try: `while None::<()>.is_none()`
error: redundant pattern matching, consider using `is_some()`
- --> $DIR/redundant_pattern_matching_option.rs:44:15
+ --> $DIR/redundant_pattern_matching_option.rs:49:15
|
LL | while let Some(_) = v.pop() {
| ----------^^^^^^^---------- help: try: `while v.pop().is_some()`
error: redundant pattern matching, consider using `is_some()`
- --> $DIR/redundant_pattern_matching_option.rs:52:5
+ --> $DIR/redundant_pattern_matching_option.rs:57:5
|
LL | / match Some(42) {
LL | | Some(_) => true,
@@ -65,7 +65,7 @@ LL | | };
| |_____^ help: try: `Some(42).is_some()`
error: redundant pattern matching, consider using `is_none()`
- --> $DIR/redundant_pattern_matching_option.rs:57:5
+ --> $DIR/redundant_pattern_matching_option.rs:62:5
|
LL | / match None::<()> {
LL | | Some(_) => false,
@@ -74,7 +74,7 @@ LL | | };
| |_____^ help: try: `None::<()>.is_none()`
error: redundant pattern matching, consider using `is_none()`
- --> $DIR/redundant_pattern_matching_option.rs:62:13
+ --> $DIR/redundant_pattern_matching_option.rs:67:13
|
LL | let _ = match None::<()> {
| _____________^
@@ -84,55 +84,55 @@ LL | | };
| |_____^ help: try: `None::<()>.is_none()`
error: redundant pattern matching, consider using `is_some()`
- --> $DIR/redundant_pattern_matching_option.rs:68:20
+ --> $DIR/redundant_pattern_matching_option.rs:73:20
|
LL | let _ = if let Some(_) = opt { true } else { false };
| -------^^^^^^^------ help: try: `if opt.is_some()`
error: redundant pattern matching, consider using `is_some()`
- --> $DIR/redundant_pattern_matching_option.rs:74:20
+ --> $DIR/redundant_pattern_matching_option.rs:79:20
|
LL | let _ = if let Some(_) = gen_opt() {
| -------^^^^^^^------------ help: try: `if gen_opt().is_some()`
error: redundant pattern matching, consider using `is_none()`
- --> $DIR/redundant_pattern_matching_option.rs:76:19
+ --> $DIR/redundant_pattern_matching_option.rs:81:19
|
LL | } else if let None = gen_opt() {
| -------^^^^------------ help: try: `if gen_opt().is_none()`
error: redundant pattern matching, consider using `is_some()`
- --> $DIR/redundant_pattern_matching_option.rs:82:12
+ --> $DIR/redundant_pattern_matching_option.rs:87:12
|
LL | if let Some(..) = gen_opt() {}
| -------^^^^^^^^------------ help: try: `if gen_opt().is_some()`
error: redundant pattern matching, consider using `is_some()`
- --> $DIR/redundant_pattern_matching_option.rs:97:12
+ --> $DIR/redundant_pattern_matching_option.rs:102:12
|
LL | if let Some(_) = Some(42) {}
| -------^^^^^^^----------- help: try: `if Some(42).is_some()`
error: redundant pattern matching, consider using `is_none()`
- --> $DIR/redundant_pattern_matching_option.rs:99:12
+ --> $DIR/redundant_pattern_matching_option.rs:104:12
|
LL | if let None = None::<()> {}
| -------^^^^------------- help: try: `if None::<()>.is_none()`
error: redundant pattern matching, consider using `is_some()`
- --> $DIR/redundant_pattern_matching_option.rs:101:15
+ --> $DIR/redundant_pattern_matching_option.rs:106:15
|
LL | while let Some(_) = Some(42) {}
| ----------^^^^^^^----------- help: try: `while Some(42).is_some()`
error: redundant pattern matching, consider using `is_none()`
- --> $DIR/redundant_pattern_matching_option.rs:103:15
+ --> $DIR/redundant_pattern_matching_option.rs:108:15
|
LL | while let None = None::<()> {}
| ----------^^^^------------- help: try: `while None::<()>.is_none()`
error: redundant pattern matching, consider using `is_some()`
- --> $DIR/redundant_pattern_matching_option.rs:105:5
+ --> $DIR/redundant_pattern_matching_option.rs:110:5
|
LL | / match Some(42) {
LL | | Some(_) => true,
@@ -141,7 +141,7 @@ LL | | };
| |_____^ help: try: `Some(42).is_some()`
error: redundant pattern matching, consider using `is_none()`
- --> $DIR/redundant_pattern_matching_option.rs:110:5
+ --> $DIR/redundant_pattern_matching_option.rs:115:5
|
LL | / match None::<()> {
LL | | Some(_) => false,
@@ -150,19 +150,19 @@ LL | | };
| |_____^ help: try: `None::<()>.is_none()`
error: redundant pattern matching, consider using `is_none()`
- --> $DIR/redundant_pattern_matching_option.rs:118:12
+ --> $DIR/redundant_pattern_matching_option.rs:123:12
|
LL | if let None = *(&None::<()>) {}
| -------^^^^----------------- help: try: `if (&None::<()>).is_none()`
error: redundant pattern matching, consider using `is_none()`
- --> $DIR/redundant_pattern_matching_option.rs:119:12
+ --> $DIR/redundant_pattern_matching_option.rs:124:12
|
LL | if let None = *&None::<()> {}
| -------^^^^--------------- help: try: `if (&None::<()>).is_none()`
error: redundant pattern matching, consider using `is_some()`
- --> $DIR/redundant_pattern_matching_option.rs:125:5
+ --> $DIR/redundant_pattern_matching_option.rs:130:5
|
LL | / match x {
LL | | Some(_) => true,
@@ -171,7 +171,7 @@ LL | | };
| |_____^ help: try: `x.is_some()`
error: redundant pattern matching, consider using `is_none()`
- --> $DIR/redundant_pattern_matching_option.rs:130:5
+ --> $DIR/redundant_pattern_matching_option.rs:135:5
|
LL | / match x {
LL | | None => true,
@@ -180,7 +180,7 @@ LL | | };
| |_____^ help: try: `x.is_none()`
error: redundant pattern matching, consider using `is_none()`
- --> $DIR/redundant_pattern_matching_option.rs:135:5
+ --> $DIR/redundant_pattern_matching_option.rs:140:5
|
LL | / match x {
LL | | Some(_) => false,
@@ -189,7 +189,7 @@ LL | | };
| |_____^ help: try: `x.is_none()`
error: redundant pattern matching, consider using `is_some()`
- --> $DIR/redundant_pattern_matching_option.rs:140:5
+ --> $DIR/redundant_pattern_matching_option.rs:145:5
|
LL | / match x {
LL | | None => false,
@@ -198,13 +198,13 @@ LL | | };
| |_____^ help: try: `x.is_some()`
error: redundant pattern matching, consider using `is_some()`
- --> $DIR/redundant_pattern_matching_option.rs:155:13
+ --> $DIR/redundant_pattern_matching_option.rs:160:13
|
LL | let _ = matches!(x, Some(_));
| ^^^^^^^^^^^^^^^^^^^^ help: try: `x.is_some()`
error: redundant pattern matching, consider using `is_none()`
- --> $DIR/redundant_pattern_matching_option.rs:157:13
+ --> $DIR/redundant_pattern_matching_option.rs:162:13
|
LL | let _ = matches!(x, None);
| ^^^^^^^^^^^^^^^^^ help: try: `x.is_none()`
diff --git a/src/tools/clippy/tests/ui/rest_pat_in_fully_bound_structs.rs b/src/tools/clippy/tests/ui/rest_pat_in_fully_bound_structs.rs
index e25609f75..51fe346d0 100644
--- a/src/tools/clippy/tests/ui/rest_pat_in_fully_bound_structs.rs
+++ b/src/tools/clippy/tests/ui/rest_pat_in_fully_bound_structs.rs
@@ -1,4 +1,5 @@
#![warn(clippy::rest_pat_in_fully_bound_structs)]
+#![allow(clippy::struct_field_names)]
struct A {
a: i32,
diff --git a/src/tools/clippy/tests/ui/rest_pat_in_fully_bound_structs.stderr b/src/tools/clippy/tests/ui/rest_pat_in_fully_bound_structs.stderr
index 2c221b4db..a62f1d0b6 100644
--- a/src/tools/clippy/tests/ui/rest_pat_in_fully_bound_structs.stderr
+++ b/src/tools/clippy/tests/ui/rest_pat_in_fully_bound_structs.stderr
@@ -1,5 +1,5 @@
error: unnecessary use of `..` pattern in struct binding. All fields were already bound
- --> $DIR/rest_pat_in_fully_bound_structs.rs:22:9
+ --> $DIR/rest_pat_in_fully_bound_structs.rs:23:9
|
LL | A { a: 5, b: 42, c: "", .. } => {}, // Lint
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -9,7 +9,7 @@ LL | A { a: 5, b: 42, c: "", .. } => {}, // Lint
= help: to override `-D warnings` add `#[allow(clippy::rest_pat_in_fully_bound_structs)]`
error: unnecessary use of `..` pattern in struct binding. All fields were already bound
- --> $DIR/rest_pat_in_fully_bound_structs.rs:24:9
+ --> $DIR/rest_pat_in_fully_bound_structs.rs:25:9
|
LL | A { a: 0, b: 0, c: "", .. } => {}, // Lint
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -17,7 +17,7 @@ LL | A { a: 0, b: 0, c: "", .. } => {}, // Lint
= help: consider removing `..` from this binding
error: unnecessary use of `..` pattern in struct binding. All fields were already bound
- --> $DIR/rest_pat_in_fully_bound_structs.rs:31:9
+ --> $DIR/rest_pat_in_fully_bound_structs.rs:32:9
|
LL | A { a: 0, b: 0, c: "", .. } => {}, // Lint
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/src/tools/clippy/tests/ui/std_instead_of_core.fixed b/src/tools/clippy/tests/ui/std_instead_of_core.fixed
index 8027c053f..a7555704f 100644
--- a/src/tools/clippy/tests/ui/std_instead_of_core.fixed
+++ b/src/tools/clippy/tests/ui/std_instead_of_core.fixed
@@ -1,8 +1,12 @@
+//@aux-build:proc_macro_derive.rs
#![warn(clippy::std_instead_of_core)]
#![allow(unused_imports)]
extern crate alloc;
+#[macro_use]
+extern crate proc_macro_derive;
+
#[warn(clippy::std_instead_of_core)]
fn std_instead_of_core() {
// Regular import
@@ -55,6 +59,13 @@ fn alloc_instead_of_core() {
//~^ ERROR: used import from `alloc` instead of `core`
}
+mod std_in_proc_macro_derive {
+ #[warn(clippy::alloc_instead_of_core)]
+ #[allow(unused)]
+ #[derive(ImplStructWithStdDisplay)]
+ struct B {}
+}
+
fn main() {
std_instead_of_core();
std_instead_of_alloc();
diff --git a/src/tools/clippy/tests/ui/std_instead_of_core.rs b/src/tools/clippy/tests/ui/std_instead_of_core.rs
index 63a096384..af7f3399f 100644
--- a/src/tools/clippy/tests/ui/std_instead_of_core.rs
+++ b/src/tools/clippy/tests/ui/std_instead_of_core.rs
@@ -1,8 +1,12 @@
+//@aux-build:proc_macro_derive.rs
#![warn(clippy::std_instead_of_core)]
#![allow(unused_imports)]
extern crate alloc;
+#[macro_use]
+extern crate proc_macro_derive;
+
#[warn(clippy::std_instead_of_core)]
fn std_instead_of_core() {
// Regular import
@@ -55,6 +59,13 @@ fn alloc_instead_of_core() {
//~^ ERROR: used import from `alloc` instead of `core`
}
+mod std_in_proc_macro_derive {
+ #[warn(clippy::alloc_instead_of_core)]
+ #[allow(unused)]
+ #[derive(ImplStructWithStdDisplay)]
+ struct B {}
+}
+
fn main() {
std_instead_of_core();
std_instead_of_alloc();
diff --git a/src/tools/clippy/tests/ui/std_instead_of_core.stderr b/src/tools/clippy/tests/ui/std_instead_of_core.stderr
index ca26f77bd..4f7bdc404 100644
--- a/src/tools/clippy/tests/ui/std_instead_of_core.stderr
+++ b/src/tools/clippy/tests/ui/std_instead_of_core.stderr
@@ -1,5 +1,5 @@
error: used import from `std` instead of `core`
- --> $DIR/std_instead_of_core.rs:9:9
+ --> $DIR/std_instead_of_core.rs:13:9
|
LL | use std::hash::Hasher;
| ^^^ help: consider importing the item from `core`: `core`
@@ -8,49 +8,49 @@ LL | use std::hash::Hasher;
= help: to override `-D warnings` add `#[allow(clippy::std_instead_of_core)]`
error: used import from `std` instead of `core`
- --> $DIR/std_instead_of_core.rs:12:11
+ --> $DIR/std_instead_of_core.rs:16:11
|
LL | use ::std::hash::Hash;
| ^^^ help: consider importing the item from `core`: `core`
error: used import from `std` instead of `core`
- --> $DIR/std_instead_of_core.rs:18:9
+ --> $DIR/std_instead_of_core.rs:22:9
|
LL | use std::fmt::{Debug, Result};
| ^^^ help: consider importing the item from `core`: `core`
error: used import from `std` instead of `core`
- --> $DIR/std_instead_of_core.rs:22:15
+ --> $DIR/std_instead_of_core.rs:26:15
|
LL | let ptr = std::ptr::null::<u32>();
| ^^^ help: consider importing the item from `core`: `core`
error: used import from `std` instead of `core`
- --> $DIR/std_instead_of_core.rs:24:21
+ --> $DIR/std_instead_of_core.rs:28:21
|
LL | let ptr_mut = ::std::ptr::null_mut::<usize>();
| ^^^ help: consider importing the item from `core`: `core`
error: used import from `std` instead of `core`
- --> $DIR/std_instead_of_core.rs:28:16
+ --> $DIR/std_instead_of_core.rs:32:16
|
LL | let cell = std::cell::Cell::new(8u32);
| ^^^ help: consider importing the item from `core`: `core`
error: used import from `std` instead of `core`
- --> $DIR/std_instead_of_core.rs:30:27
+ --> $DIR/std_instead_of_core.rs:34:27
|
LL | let cell_absolute = ::std::cell::Cell::new(8u32);
| ^^^ help: consider importing the item from `core`: `core`
error: used import from `std` instead of `core`
- --> $DIR/std_instead_of_core.rs:39:9
+ --> $DIR/std_instead_of_core.rs:43:9
|
LL | use std::iter::Iterator;
| ^^^ help: consider importing the item from `core`: `core`
error: used import from `std` instead of `alloc`
- --> $DIR/std_instead_of_core.rs:46:9
+ --> $DIR/std_instead_of_core.rs:50:9
|
LL | use std::vec;
| ^^^ help: consider importing the item from `alloc`: `alloc`
@@ -59,13 +59,13 @@ LL | use std::vec;
= help: to override `-D warnings` add `#[allow(clippy::std_instead_of_alloc)]`
error: used import from `std` instead of `alloc`
- --> $DIR/std_instead_of_core.rs:48:9
+ --> $DIR/std_instead_of_core.rs:52:9
|
LL | use std::vec::Vec;
| ^^^ help: consider importing the item from `alloc`: `alloc`
error: used import from `alloc` instead of `core`
- --> $DIR/std_instead_of_core.rs:54:9
+ --> $DIR/std_instead_of_core.rs:58:9
|
LL | use alloc::slice::from_ref;
| ^^^^^ help: consider importing the item from `core`: `core`
diff --git a/src/tools/clippy/tests/ui/struct_fields.rs b/src/tools/clippy/tests/ui/struct_fields.rs
new file mode 100644
index 000000000..8b1a1446e
--- /dev/null
+++ b/src/tools/clippy/tests/ui/struct_fields.rs
@@ -0,0 +1,331 @@
+//@aux-build:proc_macros.rs
+
+#![warn(clippy::struct_field_names)]
+#![allow(unused)]
+
+#[macro_use]
+extern crate proc_macros;
+
+struct Data1 {
+ field_data1: u8,
+ //~^ ERROR: field name ends with the struct's name
+ another: u8,
+ foo: u8,
+ bar: u8,
+}
+
+struct Data2 {
+ another: u8,
+ foo: u8,
+ data2_field: u8,
+ //~^ ERROR: field name starts with the struct's name
+ bar: u8,
+}
+
+struct StructData {
+ //~^ ERROR: all fields have the same postfix: `data`
+ movable_data: u8,
+ fixed_data: u8,
+ invisible_data: u8,
+}
+
+struct DataStruct {
+ //~^ ERROR: all fields have the same prefix: `data`
+ data_movable: u8,
+ data_fixed: u8,
+ data_invisible: u8,
+}
+
+struct DoublePrefix {
+ //~^ ERROR: all fields have the same prefix: `some_data`
+ some_data_a: bool,
+ some_data_b: bool,
+ some_data_c: bool,
+}
+
+struct DoublePostfix {
+ //~^ ERROR: all fields have the same postfix: `some_data`
+ a_some_data: bool,
+ b_some_data: bool,
+ c_some_data: bool,
+}
+
+#[allow(non_snake_case)]
+struct NotSnakeCase {
+ //~^ ERROR: all fields have the same postfix: `someData`
+ a_someData: bool,
+ b_someData: bool,
+ c_someData: bool,
+}
+#[allow(non_snake_case)]
+struct NotSnakeCase2 {
+ //~^ ERROR: all fields have the same prefix: `someData`
+ someData_c: bool,
+ someData_b: bool,
+ someData_a_b: bool,
+}
+
+// no error, threshold is 3 fiels by default
+struct Fooo {
+ foo: u8,
+ bar: u8,
+}
+
+struct NonCaps {
+ //~^ ERROR: all fields have the same prefix: `prefix`
+ prefix_的: u8,
+ prefix_tea: u8,
+ prefix_cake: u8,
+}
+
+// should not lint
+#[allow(clippy::struct_field_names)]
+pub mod allowed {
+ pub struct PubAllowed {
+ some_this: u8,
+ some_that: u8,
+ some_other_what: u8,
+ }
+}
+
+// should not lint
+struct SomeData {
+ foo: u8,
+ bar: bool,
+ path: u8,
+ answer: u8,
+}
+
+// should not lint
+pub struct NetworkLayer {
+ layer1: Vec<u8>,
+ layer2: Vec<u8>,
+ layer3: Vec<u8>,
+ layer4: Vec<u8>,
+}
+
+//should not lint
+struct North {
+ normal: u8,
+ no_left: u8,
+ no_right: u8,
+}
+
+mod issue8324_from_enum_variant_names {
+ // 8324: enum_variant_names warns even if removing the suffix would leave an empty string
+ struct Phase {
+ pre_lookup: u8,
+ lookup: u8,
+ post_lookup: u8,
+ }
+}
+
+mod issue9018_from_enum_variant_names {
+ struct DoLint {
+ //~^ ERROR: all fields have the same prefix: `_type`
+ _type_create: u8,
+ _type_read: u8,
+ _type_update: u8,
+ _type_destroy: u8,
+ }
+
+ struct DoLint2 {
+ //~^ ERROR: all fields have the same prefix: `__type`
+ __type_create: u8,
+ __type_read: u8,
+ __type_update: u8,
+ __type_destroy: u8,
+ }
+
+ struct DoLint3 {
+ //~^ ERROR: all fields have the same prefix: `___type`
+ ___type_create: u8,
+ ___type_read: u8,
+ ___type_update: u8,
+ ___type_destroy: u8,
+ }
+
+ struct DoLint4 {
+ //~^ ERROR: all fields have the same postfix: `_`
+ create_: u8,
+ read_: u8,
+ update_: u8,
+ destroy_: u8,
+ }
+
+ struct DoLint5 {
+ //~^ ERROR: all fields have the same postfix: `__`
+ create__: u8,
+ read__: u8,
+ update__: u8,
+ destroy__: u8,
+ }
+
+ struct DoLint6 {
+ //~^ ERROR: all fields have the same postfix: `___`
+ create___: u8,
+ read___: u8,
+ update___: u8,
+ destroy___: u8,
+ }
+
+ struct DoLintToo {
+ //~^ ERROR: all fields have the same postfix: `type`
+ _create_type: u8,
+ _update_type: u8,
+ _delete_type: u8,
+ }
+
+ struct DoNotLint {
+ _foo: u8,
+ _bar: u8,
+ _baz: u8,
+ }
+
+ struct DoNotLint2 {
+ __foo: u8,
+ __bar: u8,
+ __baz: u8,
+ }
+}
+
+mod allow_attributes_on_fields {
+ struct Struct {
+ #[allow(clippy::struct_field_names)]
+ struct_starts_with: u8,
+ #[allow(clippy::struct_field_names)]
+ ends_with_struct: u8,
+ foo: u8,
+ }
+}
+
+// food field should not lint
+struct Foo {
+ food: i32,
+ a: i32,
+ b: i32,
+}
+
+struct Proxy {
+ proxy: i32,
+ //~^ ERROR: field name starts with the struct's name
+ unrelated1: bool,
+ unrelated2: bool,
+}
+
+// should not lint
+pub struct RegexT {
+ __buffer: i32,
+ __allocated: i32,
+ __used: i32,
+}
+
+mod macro_tests {
+ macro_rules! mk_struct {
+ () => {
+ struct MacroStruct {
+ some_a: i32,
+ some_b: i32,
+ some_c: i32,
+ }
+ };
+ }
+ mk_struct!();
+ //~^ ERROR: all fields have the same prefix: `some`
+
+ macro_rules! mk_struct2 {
+ () => {
+ struct Macrobaz {
+ macrobaz_a: i32,
+ some_b: i32,
+ some_c: i32,
+ }
+ };
+ }
+ mk_struct2!();
+ //~^ ERROR: field name starts with the struct's name
+
+ macro_rules! mk_struct_with_names {
+ ($struct_name:ident, $field:ident) => {
+ struct $struct_name {
+ $field: i32,
+ other_something: i32,
+ other_field: i32,
+ }
+ };
+ }
+ // expands to `struct Foo { foo: i32, ... }`
+ mk_struct_with_names!(Foo, foo);
+ //~^ ERROR: field name starts with the struct's name
+
+ // expands to a struct with all fields starting with `other` but should not
+ // be linted because some fields come from the macro definition and the other from the input
+ mk_struct_with_names!(Some, other_data);
+
+ // should not lint when names come from different places
+ macro_rules! mk_struct_with_field_name {
+ ($field_name:ident) => {
+ struct Baz {
+ one: i32,
+ two: i32,
+ $field_name: i32,
+ }
+ };
+ }
+ mk_struct_with_field_name!(baz_three);
+
+ // should not lint when names come from different places
+ macro_rules! mk_struct_with_field_name {
+ ($field_name:ident) => {
+ struct Bazilisk {
+ baz_one: i32,
+ baz_two: i32,
+ $field_name: i32,
+ }
+ };
+ }
+ mk_struct_with_field_name!(baz_three);
+
+ macro_rules! mk_struct_full_def {
+ ($struct_name:ident, $field1:ident, $field2:ident, $field3:ident) => {
+ struct $struct_name {
+ $field1: i32,
+ $field2: i32,
+ $field3: i32,
+ }
+ };
+ }
+ mk_struct_full_def!(PrefixData, some_data, some_meta, some_other);
+ //~^ ERROR: all fields have the same prefix: `some`
+}
+
+// should not lint on external code
+external! {
+ struct DataExternal {
+ field_data1: u8,
+ another: u8,
+ foo: u8,
+ bar: u8,
+ }
+
+ struct NotSnakeCaseExternal {
+ someData_c: bool,
+ someData_b: bool,
+ someData_a_b: bool,
+ }
+
+ struct DoublePrefixExternal {
+ some_data_a: bool,
+ some_data_b: bool,
+ some_data_c: bool,
+ }
+
+ struct StructDataExternal {
+ movable_data: u8,
+ fixed_data: u8,
+ invisible_data: u8,
+ }
+
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/struct_fields.stderr b/src/tools/clippy/tests/ui/struct_fields.stderr
new file mode 100644
index 000000000..4ca57715b
--- /dev/null
+++ b/src/tools/clippy/tests/ui/struct_fields.stderr
@@ -0,0 +1,265 @@
+error: field name ends with the struct's name
+ --> $DIR/struct_fields.rs:10:5
+ |
+LL | field_data1: u8,
+ | ^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::struct-field-names` implied by `-D warnings`
+ = help: to override `-D warnings` add `#[allow(clippy::struct_field_names)]`
+
+error: field name starts with the struct's name
+ --> $DIR/struct_fields.rs:20:5
+ |
+LL | data2_field: u8,
+ | ^^^^^^^^^^^^^^^
+
+error: all fields have the same postfix: `data`
+ --> $DIR/struct_fields.rs:25:1
+ |
+LL | / struct StructData {
+LL | |
+LL | | movable_data: u8,
+LL | | fixed_data: u8,
+LL | | invisible_data: u8,
+LL | | }
+ | |_^
+ |
+ = help: remove the postfixes
+
+error: all fields have the same prefix: `data`
+ --> $DIR/struct_fields.rs:32:1
+ |
+LL | / struct DataStruct {
+LL | |
+LL | | data_movable: u8,
+LL | | data_fixed: u8,
+LL | | data_invisible: u8,
+LL | | }
+ | |_^
+ |
+ = help: remove the prefixes
+
+error: all fields have the same prefix: `some_data`
+ --> $DIR/struct_fields.rs:39:1
+ |
+LL | / struct DoublePrefix {
+LL | |
+LL | | some_data_a: bool,
+LL | | some_data_b: bool,
+LL | | some_data_c: bool,
+LL | | }
+ | |_^
+ |
+ = help: remove the prefixes
+
+error: all fields have the same postfix: `some_data`
+ --> $DIR/struct_fields.rs:46:1
+ |
+LL | / struct DoublePostfix {
+LL | |
+LL | | a_some_data: bool,
+LL | | b_some_data: bool,
+LL | | c_some_data: bool,
+LL | | }
+ | |_^
+ |
+ = help: remove the postfixes
+
+error: all fields have the same postfix: `someData`
+ --> $DIR/struct_fields.rs:54:1
+ |
+LL | / struct NotSnakeCase {
+LL | |
+LL | | a_someData: bool,
+LL | | b_someData: bool,
+LL | | c_someData: bool,
+LL | | }
+ | |_^
+ |
+ = help: remove the postfixes
+
+error: all fields have the same prefix: `someData`
+ --> $DIR/struct_fields.rs:61:1
+ |
+LL | / struct NotSnakeCase2 {
+LL | |
+LL | | someData_c: bool,
+LL | | someData_b: bool,
+LL | | someData_a_b: bool,
+LL | | }
+ | |_^
+ |
+ = help: remove the prefixes
+
+error: all fields have the same prefix: `prefix`
+ --> $DIR/struct_fields.rs:74:1
+ |
+LL | / struct NonCaps {
+LL | |
+LL | | prefix_的: u8,
+LL | | prefix_tea: u8,
+LL | | prefix_cake: u8,
+LL | | }
+ | |_^
+ |
+ = help: remove the prefixes
+
+error: all fields have the same prefix: `_type`
+ --> $DIR/struct_fields.rs:124:5
+ |
+LL | / struct DoLint {
+LL | |
+LL | | _type_create: u8,
+LL | | _type_read: u8,
+LL | | _type_update: u8,
+LL | | _type_destroy: u8,
+LL | | }
+ | |_____^
+ |
+ = help: remove the prefixes
+
+error: all fields have the same prefix: `__type`
+ --> $DIR/struct_fields.rs:132:5
+ |
+LL | / struct DoLint2 {
+LL | |
+LL | | __type_create: u8,
+LL | | __type_read: u8,
+LL | | __type_update: u8,
+LL | | __type_destroy: u8,
+LL | | }
+ | |_____^
+ |
+ = help: remove the prefixes
+
+error: all fields have the same prefix: `___type`
+ --> $DIR/struct_fields.rs:140:5
+ |
+LL | / struct DoLint3 {
+LL | |
+LL | | ___type_create: u8,
+LL | | ___type_read: u8,
+LL | | ___type_update: u8,
+LL | | ___type_destroy: u8,
+LL | | }
+ | |_____^
+ |
+ = help: remove the prefixes
+
+error: all fields have the same postfix: `_`
+ --> $DIR/struct_fields.rs:148:5
+ |
+LL | / struct DoLint4 {
+LL | |
+LL | | create_: u8,
+LL | | read_: u8,
+LL | | update_: u8,
+LL | | destroy_: u8,
+LL | | }
+ | |_____^
+ |
+ = help: remove the postfixes
+
+error: all fields have the same postfix: `__`
+ --> $DIR/struct_fields.rs:156:5
+ |
+LL | / struct DoLint5 {
+LL | |
+LL | | create__: u8,
+LL | | read__: u8,
+LL | | update__: u8,
+LL | | destroy__: u8,
+LL | | }
+ | |_____^
+ |
+ = help: remove the postfixes
+
+error: all fields have the same postfix: `___`
+ --> $DIR/struct_fields.rs:164:5
+ |
+LL | / struct DoLint6 {
+LL | |
+LL | | create___: u8,
+LL | | read___: u8,
+LL | | update___: u8,
+LL | | destroy___: u8,
+LL | | }
+ | |_____^
+ |
+ = help: remove the postfixes
+
+error: all fields have the same postfix: `type`
+ --> $DIR/struct_fields.rs:172:5
+ |
+LL | / struct DoLintToo {
+LL | |
+LL | | _create_type: u8,
+LL | | _update_type: u8,
+LL | | _delete_type: u8,
+LL | | }
+ | |_____^
+ |
+ = help: remove the postfixes
+
+error: field name starts with the struct's name
+ --> $DIR/struct_fields.rs:210:5
+ |
+LL | proxy: i32,
+ | ^^^^^^^^^^
+
+error: all fields have the same prefix: `some`
+ --> $DIR/struct_fields.rs:226:13
+ |
+LL | / struct MacroStruct {
+LL | | some_a: i32,
+LL | | some_b: i32,
+LL | | some_c: i32,
+LL | | }
+ | |_____________^
+...
+LL | mk_struct!();
+ | ------------ in this macro invocation
+ |
+ = help: remove the prefixes
+ = note: this error originates in the macro `mk_struct` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: field name starts with the struct's name
+ --> $DIR/struct_fields.rs:239:17
+ |
+LL | macrobaz_a: i32,
+ | ^^^^^^^^^^^^^^^
+...
+LL | mk_struct2!();
+ | ------------- in this macro invocation
+ |
+ = note: this error originates in the macro `mk_struct2` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: field name starts with the struct's name
+ --> $DIR/struct_fields.rs:251:17
+ |
+LL | $field: i32,
+ | ^^^^^^^^^^^
+...
+LL | mk_struct_with_names!(Foo, foo);
+ | ------------------------------- in this macro invocation
+ |
+ = note: this error originates in the macro `mk_struct_with_names` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: all fields have the same prefix: `some`
+ --> $DIR/struct_fields.rs:291:13
+ |
+LL | / struct $struct_name {
+LL | | $field1: i32,
+LL | | $field2: i32,
+LL | | $field3: i32,
+LL | | }
+ | |_____________^
+...
+LL | mk_struct_full_def!(PrefixData, some_data, some_meta, some_other);
+ | ----------------------------------------------------------------- in this macro invocation
+ |
+ = help: remove the prefixes
+ = note: this error originates in the macro `mk_struct_full_def` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to 21 previous errors
+
diff --git a/src/tools/clippy/tests/ui/unnecessary_fallible_conversions.fixed b/src/tools/clippy/tests/ui/unnecessary_fallible_conversions.fixed
new file mode 100644
index 000000000..9668a6b99
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unnecessary_fallible_conversions.fixed
@@ -0,0 +1,6 @@
+#![warn(clippy::unnecessary_fallible_conversions)]
+
+fn main() {
+ let _: i64 = 0i32.into();
+ let _: i64 = 0i32.into();
+}
diff --git a/src/tools/clippy/tests/ui/unnecessary_fallible_conversions.rs b/src/tools/clippy/tests/ui/unnecessary_fallible_conversions.rs
new file mode 100644
index 000000000..9fa6c08b1
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unnecessary_fallible_conversions.rs
@@ -0,0 +1,6 @@
+#![warn(clippy::unnecessary_fallible_conversions)]
+
+fn main() {
+ let _: i64 = 0i32.try_into().unwrap();
+ let _: i64 = 0i32.try_into().expect("can't happen");
+}
diff --git a/src/tools/clippy/tests/ui/unnecessary_fallible_conversions.stderr b/src/tools/clippy/tests/ui/unnecessary_fallible_conversions.stderr
new file mode 100644
index 000000000..b918fdf77
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unnecessary_fallible_conversions.stderr
@@ -0,0 +1,17 @@
+error: use of a fallible conversion when an infallible one could be used
+ --> $DIR/unnecessary_fallible_conversions.rs:4:23
+ |
+LL | let _: i64 = 0i32.try_into().unwrap();
+ | ^^^^^^^^^^^^^^^^^^^ help: use: `into()`
+ |
+ = note: `-D clippy::unnecessary-fallible-conversions` implied by `-D warnings`
+ = help: to override `-D warnings` add `#[allow(clippy::unnecessary_fallible_conversions)]`
+
+error: use of a fallible conversion when an infallible one could be used
+ --> $DIR/unnecessary_fallible_conversions.rs:5:23
+ |
+LL | let _: i64 = 0i32.try_into().expect("can't happen");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `into()`
+
+error: aborting due to 2 previous errors
+
diff --git a/src/tools/clippy/tests/ui/unnecessary_fallible_conversions_unfixable.rs b/src/tools/clippy/tests/ui/unnecessary_fallible_conversions_unfixable.rs
new file mode 100644
index 000000000..68e617cc0
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unnecessary_fallible_conversions_unfixable.rs
@@ -0,0 +1,43 @@
+//@aux-build:proc_macros.rs
+//@no-rustfix
+#![warn(clippy::unnecessary_fallible_conversions)]
+
+extern crate proc_macros;
+
+struct Foo;
+impl TryFrom<i32> for Foo {
+ type Error = ();
+ fn try_from(_: i32) -> Result<Self, Self::Error> {
+ Ok(Foo)
+ }
+}
+impl From<i64> for Foo {
+ fn from(_: i64) -> Self {
+ Foo
+ }
+}
+
+fn main() {
+ // `Foo` only implements `TryFrom<i32>` and not `From<i32>`, so don't lint
+ let _: Result<Foo, _> = 0i32.try_into();
+ let _: Result<Foo, _> = i32::try_into(0i32);
+ let _: Result<Foo, _> = Foo::try_from(0i32);
+
+ // ... it does impl From<i64> however
+ let _: Result<Foo, _> = 0i64.try_into();
+ //~^ ERROR: use of a fallible conversion when an infallible one could be used
+ let _: Result<Foo, _> = i64::try_into(0i64);
+ //~^ ERROR: use of a fallible conversion when an infallible one could be used
+ let _: Result<Foo, _> = Foo::try_from(0i64);
+ //~^ ERROR: use of a fallible conversion when an infallible one could be used
+
+ let _: Result<i64, _> = 0i32.try_into();
+ //~^ ERROR: use of a fallible conversion when an infallible one could be used
+ let _: Result<i64, _> = i32::try_into(0i32);
+ //~^ ERROR: use of a fallible conversion when an infallible one could be used
+ let _: Result<i64, _> = <_>::try_from(0i32);
+ //~^ ERROR: use of a fallible conversion when an infallible one could be used
+
+ // From a macro
+ let _: Result<i64, _> = proc_macros::external!(0i32).try_into();
+}
diff --git a/src/tools/clippy/tests/ui/unnecessary_fallible_conversions_unfixable.stderr b/src/tools/clippy/tests/ui/unnecessary_fallible_conversions_unfixable.stderr
new file mode 100644
index 000000000..286decf8f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unnecessary_fallible_conversions_unfixable.stderr
@@ -0,0 +1,41 @@
+error: use of a fallible conversion when an infallible one could be used
+ --> $DIR/unnecessary_fallible_conversions_unfixable.rs:27:34
+ |
+LL | let _: Result<Foo, _> = 0i64.try_into();
+ | ^^^^^^^^ help: use: `into`
+ |
+ = note: `-D clippy::unnecessary-fallible-conversions` implied by `-D warnings`
+ = help: to override `-D warnings` add `#[allow(clippy::unnecessary_fallible_conversions)]`
+
+error: use of a fallible conversion when an infallible one could be used
+ --> $DIR/unnecessary_fallible_conversions_unfixable.rs:29:29
+ |
+LL | let _: Result<Foo, _> = i64::try_into(0i64);
+ | ^^^^^^^^^^^^^ help: use: `Into::into`
+
+error: use of a fallible conversion when an infallible one could be used
+ --> $DIR/unnecessary_fallible_conversions_unfixable.rs:31:29
+ |
+LL | let _: Result<Foo, _> = Foo::try_from(0i64);
+ | ^^^^^^^^^^^^^ help: use: `From::from`
+
+error: use of a fallible conversion when an infallible one could be used
+ --> $DIR/unnecessary_fallible_conversions_unfixable.rs:34:34
+ |
+LL | let _: Result<i64, _> = 0i32.try_into();
+ | ^^^^^^^^ help: use: `into`
+
+error: use of a fallible conversion when an infallible one could be used
+ --> $DIR/unnecessary_fallible_conversions_unfixable.rs:36:29
+ |
+LL | let _: Result<i64, _> = i32::try_into(0i32);
+ | ^^^^^^^^^^^^^ help: use: `Into::into`
+
+error: use of a fallible conversion when an infallible one could be used
+ --> $DIR/unnecessary_fallible_conversions_unfixable.rs:38:29
+ |
+LL | let _: Result<i64, _> = <_>::try_from(0i32);
+ | ^^^^^^^^^^^^^ help: use: `From::from`
+
+error: aborting due to 6 previous errors
+
diff --git a/src/tools/clippy/tests/ui/unnecessary_lazy_eval.fixed b/src/tools/clippy/tests/ui/unnecessary_lazy_eval.fixed
index 304e7b7fd..4778eaefd 100644
--- a/src/tools/clippy/tests/ui/unnecessary_lazy_eval.fixed
+++ b/src/tools/clippy/tests/ui/unnecessary_lazy_eval.fixed
@@ -5,6 +5,7 @@
#![allow(clippy::map_identity)]
#![allow(clippy::needless_borrow)]
#![allow(clippy::unnecessary_literal_unwrap)]
+#![allow(clippy::unit_arg)]
use std::ops::Deref;
@@ -76,6 +77,8 @@ fn main() {
let _ = opt.ok_or(2);
let _ = nested_tuple_opt.unwrap_or(Some((1, 2)));
let _ = cond.then_some(astronomers_pi);
+ let _ = true.then_some({});
+ let _ = true.then_some({});
// Should lint - Builtin deref
let r = &1;
diff --git a/src/tools/clippy/tests/ui/unnecessary_lazy_eval.rs b/src/tools/clippy/tests/ui/unnecessary_lazy_eval.rs
index ddfa6bb3e..d4b7fd31b 100644
--- a/src/tools/clippy/tests/ui/unnecessary_lazy_eval.rs
+++ b/src/tools/clippy/tests/ui/unnecessary_lazy_eval.rs
@@ -5,6 +5,7 @@
#![allow(clippy::map_identity)]
#![allow(clippy::needless_borrow)]
#![allow(clippy::unnecessary_literal_unwrap)]
+#![allow(clippy::unit_arg)]
use std::ops::Deref;
@@ -76,6 +77,8 @@ fn main() {
let _ = opt.ok_or_else(|| 2);
let _ = nested_tuple_opt.unwrap_or_else(|| Some((1, 2)));
let _ = cond.then(|| astronomers_pi);
+ let _ = true.then(|| -> _ {});
+ let _ = true.then(|| {});
// Should lint - Builtin deref
let r = &1;
diff --git a/src/tools/clippy/tests/ui/unnecessary_lazy_eval.stderr b/src/tools/clippy/tests/ui/unnecessary_lazy_eval.stderr
index 4f1ca3748..1b0db4759 100644
--- a/src/tools/clippy/tests/ui/unnecessary_lazy_eval.stderr
+++ b/src/tools/clippy/tests/ui/unnecessary_lazy_eval.stderr
@@ -1,5 +1,5 @@
error: unnecessary closure used to substitute value for `Option::None`
- --> $DIR/unnecessary_lazy_eval.rs:68:13
+ --> $DIR/unnecessary_lazy_eval.rs:69:13
|
LL | let _ = opt.unwrap_or_else(|| 2);
| ^^^^--------------------
@@ -10,7 +10,7 @@ LL | let _ = opt.unwrap_or_else(|| 2);
= help: to override `-D warnings` add `#[allow(clippy::unnecessary_lazy_evaluations)]`
error: unnecessary closure used to substitute value for `Option::None`
- --> $DIR/unnecessary_lazy_eval.rs:69:13
+ --> $DIR/unnecessary_lazy_eval.rs:70:13
|
LL | let _ = opt.unwrap_or_else(|| astronomers_pi);
| ^^^^---------------------------------
@@ -18,7 +18,7 @@ LL | let _ = opt.unwrap_or_else(|| astronomers_pi);
| help: use `unwrap_or(..)` instead: `unwrap_or(astronomers_pi)`
error: unnecessary closure used to substitute value for `Option::None`
- --> $DIR/unnecessary_lazy_eval.rs:70:13
+ --> $DIR/unnecessary_lazy_eval.rs:71:13
|
LL | let _ = opt.unwrap_or_else(|| ext_str.some_field);
| ^^^^-------------------------------------
@@ -26,7 +26,7 @@ LL | let _ = opt.unwrap_or_else(|| ext_str.some_field);
| help: use `unwrap_or(..)` instead: `unwrap_or(ext_str.some_field)`
error: unnecessary closure used to substitute value for `Option::None`
- --> $DIR/unnecessary_lazy_eval.rs:72:13
+ --> $DIR/unnecessary_lazy_eval.rs:73:13
|
LL | let _ = opt.and_then(|_| ext_opt);
| ^^^^---------------------
@@ -34,7 +34,7 @@ LL | let _ = opt.and_then(|_| ext_opt);
| help: use `and(..)` instead: `and(ext_opt)`
error: unnecessary closure used to substitute value for `Option::None`
- --> $DIR/unnecessary_lazy_eval.rs:73:13
+ --> $DIR/unnecessary_lazy_eval.rs:74:13
|
LL | let _ = opt.or_else(|| ext_opt);
| ^^^^-------------------
@@ -42,7 +42,7 @@ LL | let _ = opt.or_else(|| ext_opt);
| help: use `or(..)` instead: `or(ext_opt)`
error: unnecessary closure used to substitute value for `Option::None`
- --> $DIR/unnecessary_lazy_eval.rs:74:13
+ --> $DIR/unnecessary_lazy_eval.rs:75:13
|
LL | let _ = opt.or_else(|| None);
| ^^^^----------------
@@ -50,7 +50,7 @@ LL | let _ = opt.or_else(|| None);
| help: use `or(..)` instead: `or(None)`
error: unnecessary closure used to substitute value for `Option::None`
- --> $DIR/unnecessary_lazy_eval.rs:75:13
+ --> $DIR/unnecessary_lazy_eval.rs:76:13
|
LL | let _ = opt.get_or_insert_with(|| 2);
| ^^^^------------------------
@@ -58,7 +58,7 @@ LL | let _ = opt.get_or_insert_with(|| 2);
| help: use `get_or_insert(..)` instead: `get_or_insert(2)`
error: unnecessary closure used to substitute value for `Option::None`
- --> $DIR/unnecessary_lazy_eval.rs:76:13
+ --> $DIR/unnecessary_lazy_eval.rs:77:13
|
LL | let _ = opt.ok_or_else(|| 2);
| ^^^^----------------
@@ -66,7 +66,7 @@ LL | let _ = opt.ok_or_else(|| 2);
| help: use `ok_or(..)` instead: `ok_or(2)`
error: unnecessary closure used to substitute value for `Option::None`
- --> $DIR/unnecessary_lazy_eval.rs:77:13
+ --> $DIR/unnecessary_lazy_eval.rs:78:13
|
LL | let _ = nested_tuple_opt.unwrap_or_else(|| Some((1, 2)));
| ^^^^^^^^^^^^^^^^^-------------------------------
@@ -74,15 +74,31 @@ LL | let _ = nested_tuple_opt.unwrap_or_else(|| Some((1, 2)));
| help: use `unwrap_or(..)` instead: `unwrap_or(Some((1, 2)))`
error: unnecessary closure used with `bool::then`
- --> $DIR/unnecessary_lazy_eval.rs:78:13
+ --> $DIR/unnecessary_lazy_eval.rs:79:13
|
LL | let _ = cond.then(|| astronomers_pi);
| ^^^^^-----------------------
| |
| help: use `then_some(..)` instead: `then_some(astronomers_pi)`
+error: unnecessary closure used with `bool::then`
+ --> $DIR/unnecessary_lazy_eval.rs:80:13
+ |
+LL | let _ = true.then(|| -> _ {});
+ | ^^^^^----------------
+ | |
+ | help: use `then_some(..)` instead: `then_some({})`
+
+error: unnecessary closure used with `bool::then`
+ --> $DIR/unnecessary_lazy_eval.rs:81:13
+ |
+LL | let _ = true.then(|| {});
+ | ^^^^^-----------
+ | |
+ | help: use `then_some(..)` instead: `then_some({})`
+
error: unnecessary closure used to substitute value for `Option::None`
- --> $DIR/unnecessary_lazy_eval.rs:82:13
+ --> $DIR/unnecessary_lazy_eval.rs:85:13
|
LL | let _ = Some(1).unwrap_or_else(|| *r);
| ^^^^^^^^---------------------
@@ -90,7 +106,7 @@ LL | let _ = Some(1).unwrap_or_else(|| *r);
| help: use `unwrap_or(..)` instead: `unwrap_or(*r)`
error: unnecessary closure used to substitute value for `Option::None`
- --> $DIR/unnecessary_lazy_eval.rs:84:13
+ --> $DIR/unnecessary_lazy_eval.rs:87:13
|
LL | let _ = Some(1).unwrap_or_else(|| *b);
| ^^^^^^^^---------------------
@@ -98,7 +114,7 @@ LL | let _ = Some(1).unwrap_or_else(|| *b);
| help: use `unwrap_or(..)` instead: `unwrap_or(*b)`
error: unnecessary closure used to substitute value for `Option::None`
- --> $DIR/unnecessary_lazy_eval.rs:86:13
+ --> $DIR/unnecessary_lazy_eval.rs:89:13
|
LL | let _ = Some(1).as_ref().unwrap_or_else(|| &r);
| ^^^^^^^^^^^^^^^^^---------------------
@@ -106,7 +122,7 @@ LL | let _ = Some(1).as_ref().unwrap_or_else(|| &r);
| help: use `unwrap_or(..)` instead: `unwrap_or(&r)`
error: unnecessary closure used to substitute value for `Option::None`
- --> $DIR/unnecessary_lazy_eval.rs:87:13
+ --> $DIR/unnecessary_lazy_eval.rs:90:13
|
LL | let _ = Some(1).as_ref().unwrap_or_else(|| &b);
| ^^^^^^^^^^^^^^^^^---------------------
@@ -114,7 +130,7 @@ LL | let _ = Some(1).as_ref().unwrap_or_else(|| &b);
| help: use `unwrap_or(..)` instead: `unwrap_or(&b)`
error: unnecessary closure used to substitute value for `Option::None`
- --> $DIR/unnecessary_lazy_eval.rs:90:13
+ --> $DIR/unnecessary_lazy_eval.rs:93:13
|
LL | let _ = Some(10).unwrap_or_else(|| 2);
| ^^^^^^^^^--------------------
@@ -122,7 +138,7 @@ LL | let _ = Some(10).unwrap_or_else(|| 2);
| help: use `unwrap_or(..)` instead: `unwrap_or(2)`
error: unnecessary closure used to substitute value for `Option::None`
- --> $DIR/unnecessary_lazy_eval.rs:91:13
+ --> $DIR/unnecessary_lazy_eval.rs:94:13
|
LL | let _ = Some(10).and_then(|_| ext_opt);
| ^^^^^^^^^---------------------
@@ -130,7 +146,7 @@ LL | let _ = Some(10).and_then(|_| ext_opt);
| help: use `and(..)` instead: `and(ext_opt)`
error: unnecessary closure used to substitute value for `Option::None`
- --> $DIR/unnecessary_lazy_eval.rs:92:28
+ --> $DIR/unnecessary_lazy_eval.rs:95:28
|
LL | let _: Option<usize> = None.or_else(|| ext_opt);
| ^^^^^-------------------
@@ -138,7 +154,7 @@ LL | let _: Option<usize> = None.or_else(|| ext_opt);
| help: use `or(..)` instead: `or(ext_opt)`
error: unnecessary closure used to substitute value for `Option::None`
- --> $DIR/unnecessary_lazy_eval.rs:93:13
+ --> $DIR/unnecessary_lazy_eval.rs:96:13
|
LL | let _ = None.get_or_insert_with(|| 2);
| ^^^^^------------------------
@@ -146,7 +162,7 @@ LL | let _ = None.get_or_insert_with(|| 2);
| help: use `get_or_insert(..)` instead: `get_or_insert(2)`
error: unnecessary closure used to substitute value for `Option::None`
- --> $DIR/unnecessary_lazy_eval.rs:94:35
+ --> $DIR/unnecessary_lazy_eval.rs:97:35
|
LL | let _: Result<usize, usize> = None.ok_or_else(|| 2);
| ^^^^^----------------
@@ -154,7 +170,7 @@ LL | let _: Result<usize, usize> = None.ok_or_else(|| 2);
| help: use `ok_or(..)` instead: `ok_or(2)`
error: unnecessary closure used to substitute value for `Option::None`
- --> $DIR/unnecessary_lazy_eval.rs:95:28
+ --> $DIR/unnecessary_lazy_eval.rs:98:28
|
LL | let _: Option<usize> = None.or_else(|| None);
| ^^^^^----------------
@@ -162,7 +178,7 @@ LL | let _: Option<usize> = None.or_else(|| None);
| help: use `or(..)` instead: `or(None)`
error: unnecessary closure used to substitute value for `Option::None`
- --> $DIR/unnecessary_lazy_eval.rs:98:13
+ --> $DIR/unnecessary_lazy_eval.rs:101:13
|
LL | let _ = deep.0.unwrap_or_else(|| 2);
| ^^^^^^^--------------------
@@ -170,7 +186,7 @@ LL | let _ = deep.0.unwrap_or_else(|| 2);
| help: use `unwrap_or(..)` instead: `unwrap_or(2)`
error: unnecessary closure used to substitute value for `Option::None`
- --> $DIR/unnecessary_lazy_eval.rs:99:13
+ --> $DIR/unnecessary_lazy_eval.rs:102:13
|
LL | let _ = deep.0.and_then(|_| ext_opt);
| ^^^^^^^---------------------
@@ -178,7 +194,7 @@ LL | let _ = deep.0.and_then(|_| ext_opt);
| help: use `and(..)` instead: `and(ext_opt)`
error: unnecessary closure used to substitute value for `Option::None`
- --> $DIR/unnecessary_lazy_eval.rs:100:13
+ --> $DIR/unnecessary_lazy_eval.rs:103:13
|
LL | let _ = deep.0.or_else(|| None);
| ^^^^^^^----------------
@@ -186,7 +202,7 @@ LL | let _ = deep.0.or_else(|| None);
| help: use `or(..)` instead: `or(None)`
error: unnecessary closure used to substitute value for `Option::None`
- --> $DIR/unnecessary_lazy_eval.rs:101:13
+ --> $DIR/unnecessary_lazy_eval.rs:104:13
|
LL | let _ = deep.0.get_or_insert_with(|| 2);
| ^^^^^^^------------------------
@@ -194,7 +210,7 @@ LL | let _ = deep.0.get_or_insert_with(|| 2);
| help: use `get_or_insert(..)` instead: `get_or_insert(2)`
error: unnecessary closure used to substitute value for `Option::None`
- --> $DIR/unnecessary_lazy_eval.rs:102:13
+ --> $DIR/unnecessary_lazy_eval.rs:105:13
|
LL | let _ = deep.0.ok_or_else(|| 2);
| ^^^^^^^----------------
@@ -202,7 +218,7 @@ LL | let _ = deep.0.ok_or_else(|| 2);
| help: use `ok_or(..)` instead: `ok_or(2)`
error: unnecessary closure used to substitute value for `Option::None`
- --> $DIR/unnecessary_lazy_eval.rs:132:28
+ --> $DIR/unnecessary_lazy_eval.rs:135:28
|
LL | let _: Option<usize> = None.or_else(|| Some(3));
| ^^^^^-------------------
@@ -210,7 +226,7 @@ LL | let _: Option<usize> = None.or_else(|| Some(3));
| help: use `or(..)` instead: `or(Some(3))`
error: unnecessary closure used to substitute value for `Option::None`
- --> $DIR/unnecessary_lazy_eval.rs:133:13
+ --> $DIR/unnecessary_lazy_eval.rs:136:13
|
LL | let _ = deep.0.or_else(|| Some(3));
| ^^^^^^^-------------------
@@ -218,7 +234,7 @@ LL | let _ = deep.0.or_else(|| Some(3));
| help: use `or(..)` instead: `or(Some(3))`
error: unnecessary closure used to substitute value for `Option::None`
- --> $DIR/unnecessary_lazy_eval.rs:134:13
+ --> $DIR/unnecessary_lazy_eval.rs:137:13
|
LL | let _ = opt.or_else(|| Some(3));
| ^^^^-------------------
@@ -226,7 +242,7 @@ LL | let _ = opt.or_else(|| Some(3));
| help: use `or(..)` instead: `or(Some(3))`
error: unnecessary closure used to substitute value for `Result::Err`
- --> $DIR/unnecessary_lazy_eval.rs:140:13
+ --> $DIR/unnecessary_lazy_eval.rs:143:13
|
LL | let _ = res2.unwrap_or_else(|_| 2);
| ^^^^^---------------------
@@ -234,7 +250,7 @@ LL | let _ = res2.unwrap_or_else(|_| 2);
| help: use `unwrap_or(..)` instead: `unwrap_or(2)`
error: unnecessary closure used to substitute value for `Result::Err`
- --> $DIR/unnecessary_lazy_eval.rs:141:13
+ --> $DIR/unnecessary_lazy_eval.rs:144:13
|
LL | let _ = res2.unwrap_or_else(|_| astronomers_pi);
| ^^^^^----------------------------------
@@ -242,7 +258,7 @@ LL | let _ = res2.unwrap_or_else(|_| astronomers_pi);
| help: use `unwrap_or(..)` instead: `unwrap_or(astronomers_pi)`
error: unnecessary closure used to substitute value for `Result::Err`
- --> $DIR/unnecessary_lazy_eval.rs:142:13
+ --> $DIR/unnecessary_lazy_eval.rs:145:13
|
LL | let _ = res2.unwrap_or_else(|_| ext_str.some_field);
| ^^^^^--------------------------------------
@@ -250,7 +266,7 @@ LL | let _ = res2.unwrap_or_else(|_| ext_str.some_field);
| help: use `unwrap_or(..)` instead: `unwrap_or(ext_str.some_field)`
error: unnecessary closure used to substitute value for `Result::Err`
- --> $DIR/unnecessary_lazy_eval.rs:164:35
+ --> $DIR/unnecessary_lazy_eval.rs:167:35
|
LL | let _: Result<usize, usize> = res.and_then(|_| Err(2));
| ^^^^--------------------
@@ -258,7 +274,7 @@ LL | let _: Result<usize, usize> = res.and_then(|_| Err(2));
| help: use `and(..)` instead: `and(Err(2))`
error: unnecessary closure used to substitute value for `Result::Err`
- --> $DIR/unnecessary_lazy_eval.rs:165:35
+ --> $DIR/unnecessary_lazy_eval.rs:168:35
|
LL | let _: Result<usize, usize> = res.and_then(|_| Err(astronomers_pi));
| ^^^^---------------------------------
@@ -266,7 +282,7 @@ LL | let _: Result<usize, usize> = res.and_then(|_| Err(astronomers_pi));
| help: use `and(..)` instead: `and(Err(astronomers_pi))`
error: unnecessary closure used to substitute value for `Result::Err`
- --> $DIR/unnecessary_lazy_eval.rs:166:35
+ --> $DIR/unnecessary_lazy_eval.rs:169:35
|
LL | let _: Result<usize, usize> = res.and_then(|_| Err(ext_str.some_field));
| ^^^^-------------------------------------
@@ -274,7 +290,7 @@ LL | let _: Result<usize, usize> = res.and_then(|_| Err(ext_str.some_field))
| help: use `and(..)` instead: `and(Err(ext_str.some_field))`
error: unnecessary closure used to substitute value for `Result::Err`
- --> $DIR/unnecessary_lazy_eval.rs:168:35
+ --> $DIR/unnecessary_lazy_eval.rs:171:35
|
LL | let _: Result<usize, usize> = res.or_else(|_| Ok(2));
| ^^^^------------------
@@ -282,7 +298,7 @@ LL | let _: Result<usize, usize> = res.or_else(|_| Ok(2));
| help: use `or(..)` instead: `or(Ok(2))`
error: unnecessary closure used to substitute value for `Result::Err`
- --> $DIR/unnecessary_lazy_eval.rs:169:35
+ --> $DIR/unnecessary_lazy_eval.rs:172:35
|
LL | let _: Result<usize, usize> = res.or_else(|_| Ok(astronomers_pi));
| ^^^^-------------------------------
@@ -290,7 +306,7 @@ LL | let _: Result<usize, usize> = res.or_else(|_| Ok(astronomers_pi));
| help: use `or(..)` instead: `or(Ok(astronomers_pi))`
error: unnecessary closure used to substitute value for `Result::Err`
- --> $DIR/unnecessary_lazy_eval.rs:170:35
+ --> $DIR/unnecessary_lazy_eval.rs:173:35
|
LL | let _: Result<usize, usize> = res.or_else(|_| Ok(ext_str.some_field));
| ^^^^-----------------------------------
@@ -298,7 +314,7 @@ LL | let _: Result<usize, usize> = res.or_else(|_| Ok(ext_str.some_field));
| help: use `or(..)` instead: `or(Ok(ext_str.some_field))`
error: unnecessary closure used to substitute value for `Result::Err`
- --> $DIR/unnecessary_lazy_eval.rs:171:35
+ --> $DIR/unnecessary_lazy_eval.rs:174:35
|
LL | let _: Result<usize, usize> = res.
| ___________________________________^
@@ -312,5 +328,5 @@ LL | | or_else(|_| Ok(ext_str.some_field));
| |
| help: use `or(..)` instead: `or(Ok(ext_str.some_field))`
-error: aborting due to 38 previous errors
+error: aborting due to 40 previous errors
diff --git a/src/tools/clippy/tests/ui/unnecessary_lazy_eval_unfixable.rs b/src/tools/clippy/tests/ui/unnecessary_lazy_eval_unfixable.rs
index 33685bfb7..412d4aaaf 100644
--- a/src/tools/clippy/tests/ui/unnecessary_lazy_eval_unfixable.rs
+++ b/src/tools/clippy/tests/ui/unnecessary_lazy_eval_unfixable.rs
@@ -25,3 +25,8 @@ fn main() {
let arr = [(Some(1),)];
Some(&0).and_then(|&i| arr[i].0);
}
+
+fn issue11672() {
+ // Return type annotation helps type inference and removing it can break code
+ let _ = true.then(|| -> &[u8] { &[] });
+}
diff --git a/src/tools/clippy/tests/ui/unnecessary_lazy_eval_unfixable.stderr b/src/tools/clippy/tests/ui/unnecessary_lazy_eval_unfixable.stderr
index 27fa560d4..95b02be91 100644
--- a/src/tools/clippy/tests/ui/unnecessary_lazy_eval_unfixable.stderr
+++ b/src/tools/clippy/tests/ui/unnecessary_lazy_eval_unfixable.stderr
@@ -25,5 +25,13 @@ LL | let _ = Ok(1).unwrap_or_else(|SomeStruct { .. }| 2);
| |
| help: use `unwrap_or(..)` instead: `unwrap_or(2)`
-error: aborting due to 3 previous errors
+error: unnecessary closure used with `bool::then`
+ --> $DIR/unnecessary_lazy_eval_unfixable.rs:31:13
+ |
+LL | let _ = true.then(|| -> &[u8] { &[] });
+ | ^^^^^-------------------------
+ | |
+ | help: use `then_some(..)` instead: `then_some({ &[] })`
+
+error: aborting due to 4 previous errors
diff --git a/src/tools/clippy/tests/ui/unnecessary_literal_unwrap.fixed b/src/tools/clippy/tests/ui/unnecessary_literal_unwrap.fixed
index 87df1f8cb..b17343aa9 100644
--- a/src/tools/clippy/tests/ui/unnecessary_literal_unwrap.fixed
+++ b/src/tools/clippy/tests/ui/unnecessary_literal_unwrap.fixed
@@ -23,7 +23,7 @@ fn unwrap_option_none() {
let _val: u16 = 234;
let _val: u16 = 234;
let _val: u16 = { 234 };
- let _val: u16 = { 234 };
+ let _val: u16 = { 234 };
panic!();
panic!("this always happens");
@@ -31,7 +31,7 @@ fn unwrap_option_none() {
234;
234;
{ 234 };
- { 234 };
+ { 234 };
}
fn unwrap_result_ok() {
diff --git a/src/tools/clippy/tests/ui/unnecessary_literal_unwrap.stderr b/src/tools/clippy/tests/ui/unnecessary_literal_unwrap.stderr
index 013907f59..4940091be 100644
--- a/src/tools/clippy/tests/ui/unnecessary_literal_unwrap.stderr
+++ b/src/tools/clippy/tests/ui/unnecessary_literal_unwrap.stderr
@@ -116,7 +116,7 @@ LL | let _val: u16 = None.unwrap_or_else(|| -> u16 { 234 });
help: remove the `None` and `unwrap_or_else()`
|
LL - let _val: u16 = None.unwrap_or_else(|| -> u16 { 234 });
-LL + let _val: u16 = { 234 };
+LL + let _val: u16 = { 234 };
|
error: used `unwrap()` on `None` value
@@ -187,7 +187,7 @@ LL | None::<u16>.unwrap_or_else(|| -> u16 { 234 });
help: remove the `None` and `unwrap_or_else()`
|
LL - None::<u16>.unwrap_or_else(|| -> u16 { 234 });
-LL + { 234 };
+LL + { 234 };
|
error: used `unwrap()` on `Ok` value
diff --git a/src/tools/clippy/tests/ui/unnecessary_to_owned.fixed b/src/tools/clippy/tests/ui/unnecessary_to_owned.fixed
index 67faabc53..2dd1d7466 100644
--- a/src/tools/clippy/tests/ui/unnecessary_to_owned.fixed
+++ b/src/tools/clippy/tests/ui/unnecessary_to_owned.fixed
@@ -1,4 +1,10 @@
-#![allow(clippy::needless_borrow, clippy::needless_borrows_for_generic_args, clippy::ptr_arg)]
+#![allow(
+ clippy::needless_borrow,
+ clippy::needless_borrows_for_generic_args,
+ clippy::ptr_arg,
+ clippy::manual_async_fn,
+ clippy::needless_lifetimes
+)]
#![warn(clippy::unnecessary_to_owned, clippy::redundant_clone)]
use std::borrow::Cow;
@@ -506,3 +512,18 @@ mod issue_10033 {
}
}
}
+
+mod issue_11952 {
+ use core::future::{Future, IntoFuture};
+
+ fn foo<'a, T: AsRef<[u8]>>(x: T, y: &'a i32) -> impl 'a + Future<Output = Result<(), ()>> {
+ async move {
+ let _y = y;
+ Ok(())
+ }
+ }
+
+ fn bar() {
+ IntoFuture::into_future(foo([], &0));
+ }
+}
diff --git a/src/tools/clippy/tests/ui/unnecessary_to_owned.rs b/src/tools/clippy/tests/ui/unnecessary_to_owned.rs
index 99f913642..17fad3340 100644
--- a/src/tools/clippy/tests/ui/unnecessary_to_owned.rs
+++ b/src/tools/clippy/tests/ui/unnecessary_to_owned.rs
@@ -1,4 +1,10 @@
-#![allow(clippy::needless_borrow, clippy::needless_borrows_for_generic_args, clippy::ptr_arg)]
+#![allow(
+ clippy::needless_borrow,
+ clippy::needless_borrows_for_generic_args,
+ clippy::ptr_arg,
+ clippy::manual_async_fn,
+ clippy::needless_lifetimes
+)]
#![warn(clippy::unnecessary_to_owned, clippy::redundant_clone)]
use std::borrow::Cow;
@@ -506,3 +512,18 @@ mod issue_10033 {
}
}
}
+
+mod issue_11952 {
+ use core::future::{Future, IntoFuture};
+
+ fn foo<'a, T: AsRef<[u8]>>(x: T, y: &'a i32) -> impl 'a + Future<Output = Result<(), ()>> {
+ async move {
+ let _y = y;
+ Ok(())
+ }
+ }
+
+ fn bar() {
+ IntoFuture::into_future(foo([].to_vec(), &0));
+ }
+}
diff --git a/src/tools/clippy/tests/ui/unnecessary_to_owned.stderr b/src/tools/clippy/tests/ui/unnecessary_to_owned.stderr
index d8971b51d..ad6fa422b 100644
--- a/src/tools/clippy/tests/ui/unnecessary_to_owned.stderr
+++ b/src/tools/clippy/tests/ui/unnecessary_to_owned.stderr
@@ -1,11 +1,11 @@
error: redundant clone
- --> $DIR/unnecessary_to_owned.rs:148:64
+ --> $DIR/unnecessary_to_owned.rs:154:64
|
LL | require_c_str(&CString::from_vec_with_nul(vec![0]).unwrap().to_owned());
| ^^^^^^^^^^^ help: remove this
|
note: this value is dropped without further use
- --> $DIR/unnecessary_to_owned.rs:148:20
+ --> $DIR/unnecessary_to_owned.rs:154:20
|
LL | require_c_str(&CString::from_vec_with_nul(vec![0]).unwrap().to_owned());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -13,55 +13,55 @@ LL | require_c_str(&CString::from_vec_with_nul(vec![0]).unwrap().to_owned())
= help: to override `-D warnings` add `#[allow(clippy::redundant_clone)]`
error: redundant clone
- --> $DIR/unnecessary_to_owned.rs:149:40
+ --> $DIR/unnecessary_to_owned.rs:155:40
|
LL | require_os_str(&OsString::from("x").to_os_string());
| ^^^^^^^^^^^^^^^ help: remove this
|
note: this value is dropped without further use
- --> $DIR/unnecessary_to_owned.rs:149:21
+ --> $DIR/unnecessary_to_owned.rs:155:21
|
LL | require_os_str(&OsString::from("x").to_os_string());
| ^^^^^^^^^^^^^^^^^^^
error: redundant clone
- --> $DIR/unnecessary_to_owned.rs:150:48
+ --> $DIR/unnecessary_to_owned.rs:156:48
|
LL | require_path(&std::path::PathBuf::from("x").to_path_buf());
| ^^^^^^^^^^^^^^ help: remove this
|
note: this value is dropped without further use
- --> $DIR/unnecessary_to_owned.rs:150:19
+ --> $DIR/unnecessary_to_owned.rs:156:19
|
LL | require_path(&std::path::PathBuf::from("x").to_path_buf());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: redundant clone
- --> $DIR/unnecessary_to_owned.rs:151:35
+ --> $DIR/unnecessary_to_owned.rs:157:35
|
LL | require_str(&String::from("x").to_string());
| ^^^^^^^^^^^^ help: remove this
|
note: this value is dropped without further use
- --> $DIR/unnecessary_to_owned.rs:151:18
+ --> $DIR/unnecessary_to_owned.rs:157:18
|
LL | require_str(&String::from("x").to_string());
| ^^^^^^^^^^^^^^^^^
error: redundant clone
- --> $DIR/unnecessary_to_owned.rs:152:39
+ --> $DIR/unnecessary_to_owned.rs:158:39
|
LL | require_slice(&[String::from("x")].to_owned());
| ^^^^^^^^^^^ help: remove this
|
note: this value is dropped without further use
- --> $DIR/unnecessary_to_owned.rs:152:20
+ --> $DIR/unnecessary_to_owned.rs:158:20
|
LL | require_slice(&[String::from("x")].to_owned());
| ^^^^^^^^^^^^^^^^^^^
error: unnecessary use of `into_owned`
- --> $DIR/unnecessary_to_owned.rs:57:36
+ --> $DIR/unnecessary_to_owned.rs:63:36
|
LL | require_c_str(&Cow::from(c_str).into_owned());
| ^^^^^^^^^^^^^ help: remove this
@@ -70,415 +70,415 @@ LL | require_c_str(&Cow::from(c_str).into_owned());
= help: to override `-D warnings` add `#[allow(clippy::unnecessary_to_owned)]`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:58:19
+ --> $DIR/unnecessary_to_owned.rs:64:19
|
LL | require_c_str(&c_str.to_owned());
| ^^^^^^^^^^^^^^^^^ help: use: `c_str`
error: unnecessary use of `to_os_string`
- --> $DIR/unnecessary_to_owned.rs:60:20
+ --> $DIR/unnecessary_to_owned.rs:66:20
|
LL | require_os_str(&os_str.to_os_string());
| ^^^^^^^^^^^^^^^^^^^^^^ help: use: `os_str`
error: unnecessary use of `into_owned`
- --> $DIR/unnecessary_to_owned.rs:61:38
+ --> $DIR/unnecessary_to_owned.rs:67:38
|
LL | require_os_str(&Cow::from(os_str).into_owned());
| ^^^^^^^^^^^^^ help: remove this
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:62:20
+ --> $DIR/unnecessary_to_owned.rs:68:20
|
LL | require_os_str(&os_str.to_owned());
| ^^^^^^^^^^^^^^^^^^ help: use: `os_str`
error: unnecessary use of `to_path_buf`
- --> $DIR/unnecessary_to_owned.rs:64:18
+ --> $DIR/unnecessary_to_owned.rs:70:18
|
LL | require_path(&path.to_path_buf());
| ^^^^^^^^^^^^^^^^^^^ help: use: `path`
error: unnecessary use of `into_owned`
- --> $DIR/unnecessary_to_owned.rs:65:34
+ --> $DIR/unnecessary_to_owned.rs:71:34
|
LL | require_path(&Cow::from(path).into_owned());
| ^^^^^^^^^^^^^ help: remove this
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:66:18
+ --> $DIR/unnecessary_to_owned.rs:72:18
|
LL | require_path(&path.to_owned());
| ^^^^^^^^^^^^^^^^ help: use: `path`
error: unnecessary use of `to_string`
- --> $DIR/unnecessary_to_owned.rs:68:17
+ --> $DIR/unnecessary_to_owned.rs:74:17
|
LL | require_str(&s.to_string());
| ^^^^^^^^^^^^^^ help: use: `s`
error: unnecessary use of `into_owned`
- --> $DIR/unnecessary_to_owned.rs:69:30
+ --> $DIR/unnecessary_to_owned.rs:75:30
|
LL | require_str(&Cow::from(s).into_owned());
| ^^^^^^^^^^^^^ help: remove this
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:70:17
+ --> $DIR/unnecessary_to_owned.rs:76:17
|
LL | require_str(&s.to_owned());
| ^^^^^^^^^^^^^ help: use: `s`
error: unnecessary use of `to_string`
- --> $DIR/unnecessary_to_owned.rs:71:17
+ --> $DIR/unnecessary_to_owned.rs:77:17
|
LL | require_str(&x_ref.to_string());
| ^^^^^^^^^^^^^^^^^^ help: use: `x_ref.as_ref()`
error: unnecessary use of `to_vec`
- --> $DIR/unnecessary_to_owned.rs:73:19
+ --> $DIR/unnecessary_to_owned.rs:79:19
|
LL | require_slice(&slice.to_vec());
| ^^^^^^^^^^^^^^^ help: use: `slice`
error: unnecessary use of `into_owned`
- --> $DIR/unnecessary_to_owned.rs:74:36
+ --> $DIR/unnecessary_to_owned.rs:80:36
|
LL | require_slice(&Cow::from(slice).into_owned());
| ^^^^^^^^^^^^^ help: remove this
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:75:19
+ --> $DIR/unnecessary_to_owned.rs:81:19
|
LL | require_slice(&array.to_owned());
| ^^^^^^^^^^^^^^^^^ help: use: `array.as_ref()`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:76:19
+ --> $DIR/unnecessary_to_owned.rs:82:19
|
LL | require_slice(&array_ref.to_owned());
| ^^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref.as_ref()`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:77:19
+ --> $DIR/unnecessary_to_owned.rs:83:19
|
LL | require_slice(&slice.to_owned());
| ^^^^^^^^^^^^^^^^^ help: use: `slice`
error: unnecessary use of `into_owned`
- --> $DIR/unnecessary_to_owned.rs:80:42
+ --> $DIR/unnecessary_to_owned.rs:86:42
|
LL | require_x(&Cow::<X>::Owned(x.clone()).into_owned());
| ^^^^^^^^^^^^^ help: remove this
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:83:25
+ --> $DIR/unnecessary_to_owned.rs:89:25
|
LL | require_deref_c_str(c_str.to_owned());
| ^^^^^^^^^^^^^^^^ help: use: `c_str`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:84:26
+ --> $DIR/unnecessary_to_owned.rs:90:26
|
LL | require_deref_os_str(os_str.to_owned());
| ^^^^^^^^^^^^^^^^^ help: use: `os_str`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:85:24
+ --> $DIR/unnecessary_to_owned.rs:91:24
|
LL | require_deref_path(path.to_owned());
| ^^^^^^^^^^^^^^^ help: use: `path`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:86:23
+ --> $DIR/unnecessary_to_owned.rs:92:23
|
LL | require_deref_str(s.to_owned());
| ^^^^^^^^^^^^ help: use: `s`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:87:25
+ --> $DIR/unnecessary_to_owned.rs:93:25
|
LL | require_deref_slice(slice.to_owned());
| ^^^^^^^^^^^^^^^^ help: use: `slice`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:89:30
+ --> $DIR/unnecessary_to_owned.rs:95:30
|
LL | require_impl_deref_c_str(c_str.to_owned());
| ^^^^^^^^^^^^^^^^ help: use: `c_str`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:90:31
+ --> $DIR/unnecessary_to_owned.rs:96:31
|
LL | require_impl_deref_os_str(os_str.to_owned());
| ^^^^^^^^^^^^^^^^^ help: use: `os_str`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:91:29
+ --> $DIR/unnecessary_to_owned.rs:97:29
|
LL | require_impl_deref_path(path.to_owned());
| ^^^^^^^^^^^^^^^ help: use: `path`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:92:28
+ --> $DIR/unnecessary_to_owned.rs:98:28
|
LL | require_impl_deref_str(s.to_owned());
| ^^^^^^^^^^^^ help: use: `s`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:93:30
+ --> $DIR/unnecessary_to_owned.rs:99:30
|
LL | require_impl_deref_slice(slice.to_owned());
| ^^^^^^^^^^^^^^^^ help: use: `slice`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:95:29
+ --> $DIR/unnecessary_to_owned.rs:101:29
|
LL | require_deref_str_slice(s.to_owned(), slice.to_owned());
| ^^^^^^^^^^^^ help: use: `s`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:95:43
+ --> $DIR/unnecessary_to_owned.rs:101:43
|
LL | require_deref_str_slice(s.to_owned(), slice.to_owned());
| ^^^^^^^^^^^^^^^^ help: use: `slice`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:96:29
+ --> $DIR/unnecessary_to_owned.rs:102:29
|
LL | require_deref_slice_str(slice.to_owned(), s.to_owned());
| ^^^^^^^^^^^^^^^^ help: use: `slice`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:96:47
+ --> $DIR/unnecessary_to_owned.rs:102:47
|
LL | require_deref_slice_str(slice.to_owned(), s.to_owned());
| ^^^^^^^^^^^^ help: use: `s`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:98:26
+ --> $DIR/unnecessary_to_owned.rs:104:26
|
LL | require_as_ref_c_str(c_str.to_owned());
| ^^^^^^^^^^^^^^^^ help: use: `c_str`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:99:27
+ --> $DIR/unnecessary_to_owned.rs:105:27
|
LL | require_as_ref_os_str(os_str.to_owned());
| ^^^^^^^^^^^^^^^^^ help: use: `os_str`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:100:25
+ --> $DIR/unnecessary_to_owned.rs:106:25
|
LL | require_as_ref_path(path.to_owned());
| ^^^^^^^^^^^^^^^ help: use: `path`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:101:24
+ --> $DIR/unnecessary_to_owned.rs:107:24
|
LL | require_as_ref_str(s.to_owned());
| ^^^^^^^^^^^^ help: use: `s`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:102:24
+ --> $DIR/unnecessary_to_owned.rs:108:24
|
LL | require_as_ref_str(x.to_owned());
| ^^^^^^^^^^^^ help: use: `&x`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:103:26
+ --> $DIR/unnecessary_to_owned.rs:109:26
|
LL | require_as_ref_slice(array.to_owned());
| ^^^^^^^^^^^^^^^^ help: use: `array`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:104:26
+ --> $DIR/unnecessary_to_owned.rs:110:26
|
LL | require_as_ref_slice(array_ref.to_owned());
| ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:105:26
+ --> $DIR/unnecessary_to_owned.rs:111:26
|
LL | require_as_ref_slice(slice.to_owned());
| ^^^^^^^^^^^^^^^^ help: use: `slice`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:107:31
+ --> $DIR/unnecessary_to_owned.rs:113:31
|
LL | require_impl_as_ref_c_str(c_str.to_owned());
| ^^^^^^^^^^^^^^^^ help: use: `c_str`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:108:32
+ --> $DIR/unnecessary_to_owned.rs:114:32
|
LL | require_impl_as_ref_os_str(os_str.to_owned());
| ^^^^^^^^^^^^^^^^^ help: use: `os_str`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:109:30
+ --> $DIR/unnecessary_to_owned.rs:115:30
|
LL | require_impl_as_ref_path(path.to_owned());
| ^^^^^^^^^^^^^^^ help: use: `path`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:110:29
+ --> $DIR/unnecessary_to_owned.rs:116:29
|
LL | require_impl_as_ref_str(s.to_owned());
| ^^^^^^^^^^^^ help: use: `s`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:111:29
+ --> $DIR/unnecessary_to_owned.rs:117:29
|
LL | require_impl_as_ref_str(x.to_owned());
| ^^^^^^^^^^^^ help: use: `&x`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:112:31
+ --> $DIR/unnecessary_to_owned.rs:118:31
|
LL | require_impl_as_ref_slice(array.to_owned());
| ^^^^^^^^^^^^^^^^ help: use: `array`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:113:31
+ --> $DIR/unnecessary_to_owned.rs:119:31
|
LL | require_impl_as_ref_slice(array_ref.to_owned());
| ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:114:31
+ --> $DIR/unnecessary_to_owned.rs:120:31
|
LL | require_impl_as_ref_slice(slice.to_owned());
| ^^^^^^^^^^^^^^^^ help: use: `slice`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:116:30
+ --> $DIR/unnecessary_to_owned.rs:122:30
|
LL | require_as_ref_str_slice(s.to_owned(), array.to_owned());
| ^^^^^^^^^^^^ help: use: `s`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:116:44
+ --> $DIR/unnecessary_to_owned.rs:122:44
|
LL | require_as_ref_str_slice(s.to_owned(), array.to_owned());
| ^^^^^^^^^^^^^^^^ help: use: `array`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:117:30
+ --> $DIR/unnecessary_to_owned.rs:123:30
|
LL | require_as_ref_str_slice(s.to_owned(), array_ref.to_owned());
| ^^^^^^^^^^^^ help: use: `s`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:117:44
+ --> $DIR/unnecessary_to_owned.rs:123:44
|
LL | require_as_ref_str_slice(s.to_owned(), array_ref.to_owned());
| ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:118:30
+ --> $DIR/unnecessary_to_owned.rs:124:30
|
LL | require_as_ref_str_slice(s.to_owned(), slice.to_owned());
| ^^^^^^^^^^^^ help: use: `s`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:118:44
+ --> $DIR/unnecessary_to_owned.rs:124:44
|
LL | require_as_ref_str_slice(s.to_owned(), slice.to_owned());
| ^^^^^^^^^^^^^^^^ help: use: `slice`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:119:30
+ --> $DIR/unnecessary_to_owned.rs:125:30
|
LL | require_as_ref_slice_str(array.to_owned(), s.to_owned());
| ^^^^^^^^^^^^^^^^ help: use: `array`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:119:48
+ --> $DIR/unnecessary_to_owned.rs:125:48
|
LL | require_as_ref_slice_str(array.to_owned(), s.to_owned());
| ^^^^^^^^^^^^ help: use: `s`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:120:30
+ --> $DIR/unnecessary_to_owned.rs:126:30
|
LL | require_as_ref_slice_str(array_ref.to_owned(), s.to_owned());
| ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:120:52
+ --> $DIR/unnecessary_to_owned.rs:126:52
|
LL | require_as_ref_slice_str(array_ref.to_owned(), s.to_owned());
| ^^^^^^^^^^^^ help: use: `s`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:121:30
+ --> $DIR/unnecessary_to_owned.rs:127:30
|
LL | require_as_ref_slice_str(slice.to_owned(), s.to_owned());
| ^^^^^^^^^^^^^^^^ help: use: `slice`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:121:48
+ --> $DIR/unnecessary_to_owned.rs:127:48
|
LL | require_as_ref_slice_str(slice.to_owned(), s.to_owned());
| ^^^^^^^^^^^^ help: use: `s`
error: unnecessary use of `to_string`
- --> $DIR/unnecessary_to_owned.rs:123:20
+ --> $DIR/unnecessary_to_owned.rs:129:20
|
LL | let _ = x.join(&x_ref.to_string());
| ^^^^^^^^^^^^^^^^^^ help: use: `x_ref`
error: unnecessary use of `to_vec`
- --> $DIR/unnecessary_to_owned.rs:125:13
+ --> $DIR/unnecessary_to_owned.rs:131:13
|
LL | let _ = slice.to_vec().into_iter();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:126:13
+ --> $DIR/unnecessary_to_owned.rs:132:13
|
LL | let _ = slice.to_owned().into_iter();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()`
error: unnecessary use of `to_vec`
- --> $DIR/unnecessary_to_owned.rs:127:13
+ --> $DIR/unnecessary_to_owned.rs:133:13
|
LL | let _ = [std::path::PathBuf::new()][..].to_vec().into_iter();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:128:13
+ --> $DIR/unnecessary_to_owned.rs:134:13
|
LL | let _ = [std::path::PathBuf::new()][..].to_owned().into_iter();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()`
error: unnecessary use of `to_vec`
- --> $DIR/unnecessary_to_owned.rs:130:13
+ --> $DIR/unnecessary_to_owned.rs:136:13
|
LL | let _ = IntoIterator::into_iter(slice.to_vec());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:131:13
+ --> $DIR/unnecessary_to_owned.rs:137:13
|
LL | let _ = IntoIterator::into_iter(slice.to_owned());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()`
error: unnecessary use of `to_vec`
- --> $DIR/unnecessary_to_owned.rs:132:13
+ --> $DIR/unnecessary_to_owned.rs:138:13
|
LL | let _ = IntoIterator::into_iter([std::path::PathBuf::new()][..].to_vec());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:133:13
+ --> $DIR/unnecessary_to_owned.rs:139:13
|
LL | let _ = IntoIterator::into_iter([std::path::PathBuf::new()][..].to_owned());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()`
error: unnecessary use of `to_vec`
- --> $DIR/unnecessary_to_owned.rs:195:14
+ --> $DIR/unnecessary_to_owned.rs:201:14
|
LL | for t in file_types.to_vec() {
| ^^^^^^^^^^^^^^^^^^^
@@ -494,28 +494,34 @@ LL + let path = match get_file_path(t) {
|
error: unnecessary use of `to_vec`
- --> $DIR/unnecessary_to_owned.rs:218:14
+ --> $DIR/unnecessary_to_owned.rs:224:14
|
LL | let _ = &["x"][..].to_vec().into_iter();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `["x"][..].iter().cloned()`
error: unnecessary use of `to_vec`
- --> $DIR/unnecessary_to_owned.rs:223:14
+ --> $DIR/unnecessary_to_owned.rs:229:14
|
LL | let _ = &["x"][..].to_vec().into_iter();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `["x"][..].iter().copied()`
error: unnecessary use of `to_string`
- --> $DIR/unnecessary_to_owned.rs:270:24
+ --> $DIR/unnecessary_to_owned.rs:276:24
|
LL | Box::new(build(y.to_string()))
| ^^^^^^^^^^^^^ help: use: `y`
error: unnecessary use of `to_string`
- --> $DIR/unnecessary_to_owned.rs:378:12
+ --> $DIR/unnecessary_to_owned.rs:384:12
|
LL | id("abc".to_string())
| ^^^^^^^^^^^^^^^^^ help: use: `"abc"`
-error: aborting due to 79 previous errors
+error: unnecessary use of `to_vec`
+ --> $DIR/unnecessary_to_owned.rs:527:37
+ |
+LL | IntoFuture::into_future(foo([].to_vec(), &0));
+ | ^^^^^^^^^^^ help: use: `[]`
+
+error: aborting due to 80 previous errors
diff --git a/src/tools/clippy/tests/ui/unnecessary_unsafety_doc.rs b/src/tools/clippy/tests/ui/unnecessary_unsafety_doc.rs
index 373b18470..5ad117eb8 100644
--- a/src/tools/clippy/tests/ui/unnecessary_unsafety_doc.rs
+++ b/src/tools/clippy/tests/ui/unnecessary_unsafety_doc.rs
@@ -1,6 +1,6 @@
//@aux-build:proc_macros.rs
-#![allow(clippy::let_unit_value)]
+#![allow(clippy::let_unit_value, clippy::needless_pass_by_ref_mut)]
#![warn(clippy::unnecessary_safety_doc)]
extern crate proc_macros;
diff --git a/src/tools/clippy/tests/ui/unused_async.rs b/src/tools/clippy/tests/ui/unused_async.rs
index 71722e9af..7ec8a3adb 100644
--- a/src/tools/clippy/tests/ui/unused_async.rs
+++ b/src/tools/clippy/tests/ui/unused_async.rs
@@ -1,5 +1,4 @@
#![warn(clippy::unused_async)]
-#![feature(async_fn_in_trait)]
#![allow(incomplete_features)]
use std::future::Future;
diff --git a/src/tools/clippy/tests/ui/unused_async.stderr b/src/tools/clippy/tests/ui/unused_async.stderr
index 077e8cacc..c97a76a55 100644
--- a/src/tools/clippy/tests/ui/unused_async.stderr
+++ b/src/tools/clippy/tests/ui/unused_async.stderr
@@ -1,5 +1,5 @@
error: unused `async` for function with no await statements
- --> $DIR/unused_async.rs:13:5
+ --> $DIR/unused_async.rs:12:5
|
LL | / async fn async_block_await() {
LL | |
@@ -11,7 +11,7 @@ LL | | }
|
= help: consider removing the `async` from this function
note: `await` used in an async block, which does not require the enclosing function to be `async`
- --> $DIR/unused_async.rs:16:23
+ --> $DIR/unused_async.rs:15:23
|
LL | ready(()).await;
| ^^^^^
@@ -19,7 +19,7 @@ LL | ready(()).await;
= help: to override `-D warnings` add `#[allow(clippy::unused_async)]`
error: unused `async` for function with no await statements
- --> $DIR/unused_async.rs:46:5
+ --> $DIR/unused_async.rs:45:5
|
LL | async fn f3() {}
| ^^^^^^^^^^^^^^^^
@@ -27,7 +27,7 @@ LL | async fn f3() {}
= help: consider removing the `async` from this function
error: unused `async` for function with no await statements
- --> $DIR/unused_async.rs:59:1
+ --> $DIR/unused_async.rs:58:1
|
LL | / async fn foo() -> i32 {
LL | |
@@ -38,7 +38,7 @@ LL | | }
= help: consider removing the `async` from this function
error: unused `async` for function with no await statements
- --> $DIR/unused_async.rs:71:5
+ --> $DIR/unused_async.rs:70:5
|
LL | / async fn unused(&self) -> i32 {
LL | |
diff --git a/src/tools/clippy/tests/ui/unused_enumerate_index.fixed b/src/tools/clippy/tests/ui/unused_enumerate_index.fixed
new file mode 100644
index 000000000..d079807ab
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unused_enumerate_index.fixed
@@ -0,0 +1,58 @@
+#![allow(unused)]
+#![warn(clippy::unused_enumerate_index)]
+
+use std::iter::Enumerate;
+
+fn main() {
+ let v = [1, 2, 3];
+ for x in v.iter() {
+ println!("{x}");
+ }
+
+ struct Dummy1;
+ impl Dummy1 {
+ fn enumerate(self) -> Vec<usize> {
+ vec![]
+ }
+ }
+ let dummy = Dummy1;
+ for x in dummy.enumerate() {
+ println!("{x}");
+ }
+
+ struct Dummy2;
+ impl Dummy2 {
+ fn enumerate(self) -> Enumerate<std::vec::IntoIter<usize>> {
+ vec![1, 2].into_iter().enumerate()
+ }
+ }
+ let dummy = Dummy2;
+ for (_, x) in dummy.enumerate() {
+ println!("{x}");
+ }
+
+ let mut with_used_iterator = [1, 2, 3].into_iter().enumerate();
+ with_used_iterator.next();
+ for (_, x) in with_used_iterator {
+ println!("{x}");
+ }
+
+ struct Dummy3(std::vec::IntoIter<usize>);
+
+ impl Iterator for Dummy3 {
+ type Item = usize;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ self.0.next()
+ }
+
+ fn size_hint(&self) -> (usize, Option<usize>) {
+ self.0.size_hint()
+ }
+ }
+
+ let dummy = Dummy3(vec![1, 2, 3].into_iter());
+ for x in dummy {
+ println!("{x}");
+ }
+}
diff --git a/src/tools/clippy/tests/ui/unused_enumerate_index.rs b/src/tools/clippy/tests/ui/unused_enumerate_index.rs
new file mode 100644
index 000000000..2d524da76
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unused_enumerate_index.rs
@@ -0,0 +1,58 @@
+#![allow(unused)]
+#![warn(clippy::unused_enumerate_index)]
+
+use std::iter::Enumerate;
+
+fn main() {
+ let v = [1, 2, 3];
+ for (_, x) in v.iter().enumerate() {
+ println!("{x}");
+ }
+
+ struct Dummy1;
+ impl Dummy1 {
+ fn enumerate(self) -> Vec<usize> {
+ vec![]
+ }
+ }
+ let dummy = Dummy1;
+ for x in dummy.enumerate() {
+ println!("{x}");
+ }
+
+ struct Dummy2;
+ impl Dummy2 {
+ fn enumerate(self) -> Enumerate<std::vec::IntoIter<usize>> {
+ vec![1, 2].into_iter().enumerate()
+ }
+ }
+ let dummy = Dummy2;
+ for (_, x) in dummy.enumerate() {
+ println!("{x}");
+ }
+
+ let mut with_used_iterator = [1, 2, 3].into_iter().enumerate();
+ with_used_iterator.next();
+ for (_, x) in with_used_iterator {
+ println!("{x}");
+ }
+
+ struct Dummy3(std::vec::IntoIter<usize>);
+
+ impl Iterator for Dummy3 {
+ type Item = usize;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ self.0.next()
+ }
+
+ fn size_hint(&self) -> (usize, Option<usize>) {
+ self.0.size_hint()
+ }
+ }
+
+ let dummy = Dummy3(vec![1, 2, 3].into_iter());
+ for (_, x) in dummy.enumerate() {
+ println!("{x}");
+ }
+}
diff --git a/src/tools/clippy/tests/ui/unused_enumerate_index.stderr b/src/tools/clippy/tests/ui/unused_enumerate_index.stderr
new file mode 100644
index 000000000..b575fbbc4
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unused_enumerate_index.stderr
@@ -0,0 +1,26 @@
+error: you seem to use `.enumerate()` and immediately discard the index
+ --> $DIR/unused_enumerate_index.rs:8:19
+ |
+LL | for (_, x) in v.iter().enumerate() {
+ | ^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::unused-enumerate-index` implied by `-D warnings`
+ = help: to override `-D warnings` add `#[allow(clippy::unused_enumerate_index)]`
+help: remove the `.enumerate()` call
+ |
+LL | for x in v.iter() {
+ | ~ ~~~~~~~~
+
+error: you seem to use `.enumerate()` and immediately discard the index
+ --> $DIR/unused_enumerate_index.rs:55:19
+ |
+LL | for (_, x) in dummy.enumerate() {
+ | ^^^^^^^^^^^^^^^^^
+ |
+help: remove the `.enumerate()` call
+ |
+LL | for x in dummy {
+ | ~ ~~~~~
+
+error: aborting due to 2 previous errors
+
diff --git a/src/tools/clippy/tests/ui/useless_conversion_try.rs b/src/tools/clippy/tests/ui/useless_conversion_try.rs
index a5feefbe0..803d3b39f 100644
--- a/src/tools/clippy/tests/ui/useless_conversion_try.rs
+++ b/src/tools/clippy/tests/ui/useless_conversion_try.rs
@@ -1,5 +1,5 @@
#![deny(clippy::useless_conversion)]
-#![allow(clippy::needless_if)]
+#![allow(clippy::needless_if, clippy::unnecessary_fallible_conversions)]
fn test_generic<T: Copy>(val: T) -> T {
let _ = T::try_from(val).unwrap();
diff --git a/src/tools/clippy/tests/ui/waker_clone_wake.fixed b/src/tools/clippy/tests/ui/waker_clone_wake.fixed
new file mode 100644
index 000000000..9c02b9a90
--- /dev/null
+++ b/src/tools/clippy/tests/ui/waker_clone_wake.fixed
@@ -0,0 +1,29 @@
+#[derive(Clone)]
+pub struct Custom;
+
+impl Custom {
+ pub fn wake(self) {}
+}
+
+macro_rules! mac {
+ ($cx:ident) => {
+ $cx.waker()
+ };
+}
+
+pub fn wake(cx: &mut std::task::Context) {
+ cx.waker().wake_by_ref();
+
+ mac!(cx).wake_by_ref();
+}
+
+pub fn no_lint(cx: &mut std::task::Context, c: &Custom) {
+ c.clone().wake();
+
+ let w = cx.waker().clone();
+ w.wake();
+
+ cx.waker().clone().wake_by_ref();
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/waker_clone_wake.rs b/src/tools/clippy/tests/ui/waker_clone_wake.rs
new file mode 100644
index 000000000..edc3bbd8f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/waker_clone_wake.rs
@@ -0,0 +1,29 @@
+#[derive(Clone)]
+pub struct Custom;
+
+impl Custom {
+ pub fn wake(self) {}
+}
+
+macro_rules! mac {
+ ($cx:ident) => {
+ $cx.waker()
+ };
+}
+
+pub fn wake(cx: &mut std::task::Context) {
+ cx.waker().clone().wake();
+
+ mac!(cx).clone().wake();
+}
+
+pub fn no_lint(cx: &mut std::task::Context, c: &Custom) {
+ c.clone().wake();
+
+ let w = cx.waker().clone();
+ w.wake();
+
+ cx.waker().clone().wake_by_ref();
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/waker_clone_wake.stderr b/src/tools/clippy/tests/ui/waker_clone_wake.stderr
new file mode 100644
index 000000000..f1abf4d9c
--- /dev/null
+++ b/src/tools/clippy/tests/ui/waker_clone_wake.stderr
@@ -0,0 +1,17 @@
+error: cloning a `Waker` only to wake it
+ --> $DIR/waker_clone_wake.rs:15:5
+ |
+LL | cx.waker().clone().wake();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `cx.waker().wake_by_ref()`
+ |
+ = note: `-D clippy::waker-clone-wake` implied by `-D warnings`
+ = help: to override `-D warnings` add `#[allow(clippy::waker_clone_wake)]`
+
+error: cloning a `Waker` only to wake it
+ --> $DIR/waker_clone_wake.rs:17:5
+ |
+LL | mac!(cx).clone().wake();
+ | ^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `mac!(cx).wake_by_ref()`
+
+error: aborting due to 2 previous errors
+
diff --git a/src/tools/clippy/tests/ui/wildcard_imports.fixed b/src/tools/clippy/tests/ui/wildcard_imports.fixed
index 2828f9d04..6fdd728b9 100644
--- a/src/tools/clippy/tests/ui/wildcard_imports.fixed
+++ b/src/tools/clippy/tests/ui/wildcard_imports.fixed
@@ -69,6 +69,34 @@ mod struct_mod {
}
}
+// issue 9942
+mod underscore_mod {
+ // allow use of `deref` so that `clippy --fix` includes `Deref`.
+ #![allow(noop_method_call)]
+
+ mod exports_underscore {
+ pub use std::ops::Deref as _;
+ pub fn dummy() {}
+ }
+
+ mod exports_underscore_ish {
+ pub use std::ops::Deref as _Deref;
+ pub fn dummy() {}
+ }
+
+ fn does_not_lint() {
+ use self::exports_underscore::*;
+ let _ = (&0).deref();
+ dummy();
+ }
+
+ fn does_lint() {
+ use self::exports_underscore_ish::{_Deref, dummy};
+ let _ = (&0).deref();
+ dummy();
+ }
+}
+
fn main() {
foo();
multi_foo();
diff --git a/src/tools/clippy/tests/ui/wildcard_imports.rs b/src/tools/clippy/tests/ui/wildcard_imports.rs
index cbe70e505..20e06d4b3 100644
--- a/src/tools/clippy/tests/ui/wildcard_imports.rs
+++ b/src/tools/clippy/tests/ui/wildcard_imports.rs
@@ -69,6 +69,34 @@ mod struct_mod {
}
}
+// issue 9942
+mod underscore_mod {
+ // allow use of `deref` so that `clippy --fix` includes `Deref`.
+ #![allow(noop_method_call)]
+
+ mod exports_underscore {
+ pub use std::ops::Deref as _;
+ pub fn dummy() {}
+ }
+
+ mod exports_underscore_ish {
+ pub use std::ops::Deref as _Deref;
+ pub fn dummy() {}
+ }
+
+ fn does_not_lint() {
+ use self::exports_underscore::*;
+ let _ = (&0).deref();
+ dummy();
+ }
+
+ fn does_lint() {
+ use self::exports_underscore_ish::*;
+ let _ = (&0).deref();
+ dummy();
+ }
+}
+
fn main() {
foo();
multi_foo();
diff --git a/src/tools/clippy/tests/ui/wildcard_imports.stderr b/src/tools/clippy/tests/ui/wildcard_imports.stderr
index 3c750815b..01a541477 100644
--- a/src/tools/clippy/tests/ui/wildcard_imports.stderr
+++ b/src/tools/clippy/tests/ui/wildcard_imports.stderr
@@ -38,55 +38,61 @@ LL | use wildcard_imports_helper::*;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}`
error: usage of wildcard import
- --> $DIR/wildcard_imports.rs:97:13
+ --> $DIR/wildcard_imports.rs:94:13
+ |
+LL | use self::exports_underscore_ish::*;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `self::exports_underscore_ish::{_Deref, dummy}`
+
+error: usage of wildcard import
+ --> $DIR/wildcard_imports.rs:125:13
|
LL | use crate::fn_mod::*;
| ^^^^^^^^^^^^^^^^ help: try: `crate::fn_mod::foo`
error: usage of wildcard import
- --> $DIR/wildcard_imports.rs:103:75
+ --> $DIR/wildcard_imports.rs:131:75
|
LL | use wildcard_imports_helper::inner::inner_for_self_import::{self, *};
| ^ help: try: `inner_extern_foo`
error: usage of wildcard import
- --> $DIR/wildcard_imports.rs:104:13
+ --> $DIR/wildcard_imports.rs:132:13
|
LL | use wildcard_imports_helper::*;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}`
error: usage of wildcard import
- --> $DIR/wildcard_imports.rs:116:20
+ --> $DIR/wildcard_imports.rs:144:20
|
LL | use self::{inner::*, inner2::*};
| ^^^^^^^^ help: try: `inner::inner_foo`
error: usage of wildcard import
- --> $DIR/wildcard_imports.rs:116:30
+ --> $DIR/wildcard_imports.rs:144:30
|
LL | use self::{inner::*, inner2::*};
| ^^^^^^^^^ help: try: `inner2::inner_bar`
error: usage of wildcard import
- --> $DIR/wildcard_imports.rs:123:13
+ --> $DIR/wildcard_imports.rs:151:13
|
LL | use wildcard_imports_helper::*;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternExportedEnum, ExternExportedStruct, extern_exported}`
error: usage of wildcard import
- --> $DIR/wildcard_imports.rs:152:9
+ --> $DIR/wildcard_imports.rs:180:9
|
LL | use crate::in_fn_test::*;
| ^^^^^^^^^^^^^^^^^^^^ help: try: `crate::in_fn_test::{ExportedEnum, ExportedStruct, exported}`
error: usage of wildcard import
- --> $DIR/wildcard_imports.rs:161:9
+ --> $DIR/wildcard_imports.rs:189:9
|
LL | use crate:: in_fn_test:: * ;
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate:: in_fn_test::exported`
error: usage of wildcard import
- --> $DIR/wildcard_imports.rs:162:9
+ --> $DIR/wildcard_imports.rs:190:9
|
LL | use crate:: fn_mod::
| _________^
@@ -94,40 +100,40 @@ LL | | *;
| |_________^ help: try: `crate:: fn_mod::foo`
error: usage of wildcard import
- --> $DIR/wildcard_imports.rs:173:13
+ --> $DIR/wildcard_imports.rs:201:13
|
LL | use super::*;
| ^^^^^^^^ help: try: `super::foofoo`
error: usage of wildcard import
- --> $DIR/wildcard_imports.rs:208:17
+ --> $DIR/wildcard_imports.rs:236:17
|
LL | use super::*;
| ^^^^^^^^ help: try: `super::insidefoo`
error: usage of wildcard import
- --> $DIR/wildcard_imports.rs:216:13
+ --> $DIR/wildcard_imports.rs:244:13
|
LL | use crate::super_imports::*;
| ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate::super_imports::foofoo`
error: usage of wildcard import
- --> $DIR/wildcard_imports.rs:225:17
+ --> $DIR/wildcard_imports.rs:253:17
|
LL | use super::super::*;
| ^^^^^^^^^^^^^^^ help: try: `super::super::foofoo`
error: usage of wildcard import
- --> $DIR/wildcard_imports.rs:234:13
+ --> $DIR/wildcard_imports.rs:262:13
|
LL | use super::super::super_imports::*;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `super::super::super_imports::foofoo`
error: usage of wildcard import
- --> $DIR/wildcard_imports.rs:242:13
+ --> $DIR/wildcard_imports.rs:270:13
|
LL | use super::*;
| ^^^^^^^^ help: try: `super::foofoo`
-error: aborting due to 21 previous errors
+error: aborting due to 22 previous errors
diff --git a/src/tools/clippy/tests/ui/wildcard_imports_2021.edition2018.fixed b/src/tools/clippy/tests/ui/wildcard_imports_2021.edition2018.fixed
index b27281fa2..6a9fe007d 100644
--- a/src/tools/clippy/tests/ui/wildcard_imports_2021.edition2018.fixed
+++ b/src/tools/clippy/tests/ui/wildcard_imports_2021.edition2018.fixed
@@ -64,6 +64,34 @@ mod struct_mod {
}
}
+// issue 9942
+mod underscore_mod {
+ // allow use of `deref` so that `clippy --fix` includes `Deref`.
+ #![allow(noop_method_call)]
+
+ mod exports_underscore {
+ pub use std::ops::Deref as _;
+ pub fn dummy() {}
+ }
+
+ mod exports_underscore_ish {
+ pub use std::ops::Deref as _Deref;
+ pub fn dummy() {}
+ }
+
+ fn does_not_lint() {
+ use exports_underscore::*;
+ let _ = (&0).deref();
+ dummy();
+ }
+
+ fn does_lint() {
+ use exports_underscore_ish::{_Deref, dummy};
+ let _ = (&0).deref();
+ dummy();
+ }
+}
+
fn main() {
foo();
multi_foo();
diff --git a/src/tools/clippy/tests/ui/wildcard_imports_2021.edition2018.stderr b/src/tools/clippy/tests/ui/wildcard_imports_2021.edition2018.stderr
index 709a665d6..e39f240a4 100644
--- a/src/tools/clippy/tests/ui/wildcard_imports_2021.edition2018.stderr
+++ b/src/tools/clippy/tests/ui/wildcard_imports_2021.edition2018.stderr
@@ -38,55 +38,61 @@ LL | use wildcard_imports_helper::*;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}`
error: usage of wildcard import
- --> $DIR/wildcard_imports_2021.rs:91:13
+ --> $DIR/wildcard_imports_2021.rs:89:13
+ |
+LL | use exports_underscore_ish::*;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `exports_underscore_ish::{_Deref, dummy}`
+
+error: usage of wildcard import
+ --> $DIR/wildcard_imports_2021.rs:119:13
|
LL | use crate::fn_mod::*;
| ^^^^^^^^^^^^^^^^ help: try: `crate::fn_mod::foo`
error: usage of wildcard import
- --> $DIR/wildcard_imports_2021.rs:97:75
+ --> $DIR/wildcard_imports_2021.rs:125:75
|
LL | use wildcard_imports_helper::inner::inner_for_self_import::{self, *};
| ^ help: try: `inner_extern_foo`
error: usage of wildcard import
- --> $DIR/wildcard_imports_2021.rs:98:13
+ --> $DIR/wildcard_imports_2021.rs:126:13
|
LL | use wildcard_imports_helper::*;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}`
error: usage of wildcard import
- --> $DIR/wildcard_imports_2021.rs:110:20
+ --> $DIR/wildcard_imports_2021.rs:138:20
|
LL | use self::{inner::*, inner2::*};
| ^^^^^^^^ help: try: `inner::inner_foo`
error: usage of wildcard import
- --> $DIR/wildcard_imports_2021.rs:110:30
+ --> $DIR/wildcard_imports_2021.rs:138:30
|
LL | use self::{inner::*, inner2::*};
| ^^^^^^^^^ help: try: `inner2::inner_bar`
error: usage of wildcard import
- --> $DIR/wildcard_imports_2021.rs:117:13
+ --> $DIR/wildcard_imports_2021.rs:145:13
|
LL | use wildcard_imports_helper::*;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternExportedEnum, ExternExportedStruct, extern_exported}`
error: usage of wildcard import
- --> $DIR/wildcard_imports_2021.rs:146:9
+ --> $DIR/wildcard_imports_2021.rs:174:9
|
LL | use crate::in_fn_test::*;
| ^^^^^^^^^^^^^^^^^^^^ help: try: `crate::in_fn_test::{ExportedEnum, ExportedStruct, exported}`
error: usage of wildcard import
- --> $DIR/wildcard_imports_2021.rs:155:9
+ --> $DIR/wildcard_imports_2021.rs:183:9
|
LL | use crate:: in_fn_test:: * ;
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate:: in_fn_test::exported`
error: usage of wildcard import
- --> $DIR/wildcard_imports_2021.rs:156:9
+ --> $DIR/wildcard_imports_2021.rs:184:9
|
LL | use crate:: fn_mod::
| _________^
@@ -94,40 +100,40 @@ LL | | *;
| |_________^ help: try: `crate:: fn_mod::foo`
error: usage of wildcard import
- --> $DIR/wildcard_imports_2021.rs:167:13
+ --> $DIR/wildcard_imports_2021.rs:195:13
|
LL | use super::*;
| ^^^^^^^^ help: try: `super::foofoo`
error: usage of wildcard import
- --> $DIR/wildcard_imports_2021.rs:202:17
+ --> $DIR/wildcard_imports_2021.rs:230:17
|
LL | use super::*;
| ^^^^^^^^ help: try: `super::insidefoo`
error: usage of wildcard import
- --> $DIR/wildcard_imports_2021.rs:210:13
+ --> $DIR/wildcard_imports_2021.rs:238:13
|
LL | use crate::super_imports::*;
| ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate::super_imports::foofoo`
error: usage of wildcard import
- --> $DIR/wildcard_imports_2021.rs:219:17
+ --> $DIR/wildcard_imports_2021.rs:247:17
|
LL | use super::super::*;
| ^^^^^^^^^^^^^^^ help: try: `super::super::foofoo`
error: usage of wildcard import
- --> $DIR/wildcard_imports_2021.rs:228:13
+ --> $DIR/wildcard_imports_2021.rs:256:13
|
LL | use super::super::super_imports::*;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `super::super::super_imports::foofoo`
error: usage of wildcard import
- --> $DIR/wildcard_imports_2021.rs:236:13
+ --> $DIR/wildcard_imports_2021.rs:264:13
|
LL | use super::*;
| ^^^^^^^^ help: try: `super::foofoo`
-error: aborting due to 21 previous errors
+error: aborting due to 22 previous errors
diff --git a/src/tools/clippy/tests/ui/wildcard_imports_2021.edition2021.fixed b/src/tools/clippy/tests/ui/wildcard_imports_2021.edition2021.fixed
index b27281fa2..6a9fe007d 100644
--- a/src/tools/clippy/tests/ui/wildcard_imports_2021.edition2021.fixed
+++ b/src/tools/clippy/tests/ui/wildcard_imports_2021.edition2021.fixed
@@ -64,6 +64,34 @@ mod struct_mod {
}
}
+// issue 9942
+mod underscore_mod {
+ // allow use of `deref` so that `clippy --fix` includes `Deref`.
+ #![allow(noop_method_call)]
+
+ mod exports_underscore {
+ pub use std::ops::Deref as _;
+ pub fn dummy() {}
+ }
+
+ mod exports_underscore_ish {
+ pub use std::ops::Deref as _Deref;
+ pub fn dummy() {}
+ }
+
+ fn does_not_lint() {
+ use exports_underscore::*;
+ let _ = (&0).deref();
+ dummy();
+ }
+
+ fn does_lint() {
+ use exports_underscore_ish::{_Deref, dummy};
+ let _ = (&0).deref();
+ dummy();
+ }
+}
+
fn main() {
foo();
multi_foo();
diff --git a/src/tools/clippy/tests/ui/wildcard_imports_2021.edition2021.stderr b/src/tools/clippy/tests/ui/wildcard_imports_2021.edition2021.stderr
index 709a665d6..e39f240a4 100644
--- a/src/tools/clippy/tests/ui/wildcard_imports_2021.edition2021.stderr
+++ b/src/tools/clippy/tests/ui/wildcard_imports_2021.edition2021.stderr
@@ -38,55 +38,61 @@ LL | use wildcard_imports_helper::*;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}`
error: usage of wildcard import
- --> $DIR/wildcard_imports_2021.rs:91:13
+ --> $DIR/wildcard_imports_2021.rs:89:13
+ |
+LL | use exports_underscore_ish::*;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `exports_underscore_ish::{_Deref, dummy}`
+
+error: usage of wildcard import
+ --> $DIR/wildcard_imports_2021.rs:119:13
|
LL | use crate::fn_mod::*;
| ^^^^^^^^^^^^^^^^ help: try: `crate::fn_mod::foo`
error: usage of wildcard import
- --> $DIR/wildcard_imports_2021.rs:97:75
+ --> $DIR/wildcard_imports_2021.rs:125:75
|
LL | use wildcard_imports_helper::inner::inner_for_self_import::{self, *};
| ^ help: try: `inner_extern_foo`
error: usage of wildcard import
- --> $DIR/wildcard_imports_2021.rs:98:13
+ --> $DIR/wildcard_imports_2021.rs:126:13
|
LL | use wildcard_imports_helper::*;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}`
error: usage of wildcard import
- --> $DIR/wildcard_imports_2021.rs:110:20
+ --> $DIR/wildcard_imports_2021.rs:138:20
|
LL | use self::{inner::*, inner2::*};
| ^^^^^^^^ help: try: `inner::inner_foo`
error: usage of wildcard import
- --> $DIR/wildcard_imports_2021.rs:110:30
+ --> $DIR/wildcard_imports_2021.rs:138:30
|
LL | use self::{inner::*, inner2::*};
| ^^^^^^^^^ help: try: `inner2::inner_bar`
error: usage of wildcard import
- --> $DIR/wildcard_imports_2021.rs:117:13
+ --> $DIR/wildcard_imports_2021.rs:145:13
|
LL | use wildcard_imports_helper::*;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternExportedEnum, ExternExportedStruct, extern_exported}`
error: usage of wildcard import
- --> $DIR/wildcard_imports_2021.rs:146:9
+ --> $DIR/wildcard_imports_2021.rs:174:9
|
LL | use crate::in_fn_test::*;
| ^^^^^^^^^^^^^^^^^^^^ help: try: `crate::in_fn_test::{ExportedEnum, ExportedStruct, exported}`
error: usage of wildcard import
- --> $DIR/wildcard_imports_2021.rs:155:9
+ --> $DIR/wildcard_imports_2021.rs:183:9
|
LL | use crate:: in_fn_test:: * ;
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate:: in_fn_test::exported`
error: usage of wildcard import
- --> $DIR/wildcard_imports_2021.rs:156:9
+ --> $DIR/wildcard_imports_2021.rs:184:9
|
LL | use crate:: fn_mod::
| _________^
@@ -94,40 +100,40 @@ LL | | *;
| |_________^ help: try: `crate:: fn_mod::foo`
error: usage of wildcard import
- --> $DIR/wildcard_imports_2021.rs:167:13
+ --> $DIR/wildcard_imports_2021.rs:195:13
|
LL | use super::*;
| ^^^^^^^^ help: try: `super::foofoo`
error: usage of wildcard import
- --> $DIR/wildcard_imports_2021.rs:202:17
+ --> $DIR/wildcard_imports_2021.rs:230:17
|
LL | use super::*;
| ^^^^^^^^ help: try: `super::insidefoo`
error: usage of wildcard import
- --> $DIR/wildcard_imports_2021.rs:210:13
+ --> $DIR/wildcard_imports_2021.rs:238:13
|
LL | use crate::super_imports::*;
| ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate::super_imports::foofoo`
error: usage of wildcard import
- --> $DIR/wildcard_imports_2021.rs:219:17
+ --> $DIR/wildcard_imports_2021.rs:247:17
|
LL | use super::super::*;
| ^^^^^^^^^^^^^^^ help: try: `super::super::foofoo`
error: usage of wildcard import
- --> $DIR/wildcard_imports_2021.rs:228:13
+ --> $DIR/wildcard_imports_2021.rs:256:13
|
LL | use super::super::super_imports::*;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `super::super::super_imports::foofoo`
error: usage of wildcard import
- --> $DIR/wildcard_imports_2021.rs:236:13
+ --> $DIR/wildcard_imports_2021.rs:264:13
|
LL | use super::*;
| ^^^^^^^^ help: try: `super::foofoo`
-error: aborting due to 21 previous errors
+error: aborting due to 22 previous errors
diff --git a/src/tools/clippy/tests/ui/wildcard_imports_2021.rs b/src/tools/clippy/tests/ui/wildcard_imports_2021.rs
index 7dd2103ec..18ebc0f51 100644
--- a/src/tools/clippy/tests/ui/wildcard_imports_2021.rs
+++ b/src/tools/clippy/tests/ui/wildcard_imports_2021.rs
@@ -64,6 +64,34 @@ mod struct_mod {
}
}
+// issue 9942
+mod underscore_mod {
+ // allow use of `deref` so that `clippy --fix` includes `Deref`.
+ #![allow(noop_method_call)]
+
+ mod exports_underscore {
+ pub use std::ops::Deref as _;
+ pub fn dummy() {}
+ }
+
+ mod exports_underscore_ish {
+ pub use std::ops::Deref as _Deref;
+ pub fn dummy() {}
+ }
+
+ fn does_not_lint() {
+ use exports_underscore::*;
+ let _ = (&0).deref();
+ dummy();
+ }
+
+ fn does_lint() {
+ use exports_underscore_ish::*;
+ let _ = (&0).deref();
+ dummy();
+ }
+}
+
fn main() {
foo();
multi_foo();
diff --git a/src/tools/clippy/tests/ui/write_literal.fixed b/src/tools/clippy/tests/ui/write_literal.fixed
index ee577574d..3d216b76c 100644
--- a/src/tools/clippy/tests/ui/write_literal.fixed
+++ b/src/tools/clippy/tests/ui/write_literal.fixed
@@ -43,16 +43,22 @@ fn main() {
// throw a warning
writeln!(v, "hello world");
//~^ ERROR: literal with an empty format string
- //~| ERROR: literal with an empty format string
writeln!(v, "world hello");
//~^ ERROR: literal with an empty format string
- //~| ERROR: literal with an empty format string
// named args shouldn't change anything either
writeln!(v, "hello world");
//~^ ERROR: literal with an empty format string
- //~| ERROR: literal with an empty format string
writeln!(v, "world hello");
//~^ ERROR: literal with an empty format string
- //~| ERROR: literal with an empty format string
+
+ // #10128
+ writeln!(v, "hello {0} world", 2);
+ //~^ ERROR: literal with an empty format string
+ writeln!(v, "world {0} hello", 2);
+ //~^ ERROR: literal with an empty format string
+ writeln!(v, "hello {0} {1}, {bar}", 2, 3, bar = 4);
+ //~^ ERROR: literal with an empty format string
+ writeln!(v, "hello {0} {1}, world {2}", 2, 3, 4);
+ //~^ ERROR: literal with an empty format string
}
diff --git a/src/tools/clippy/tests/ui/write_literal.rs b/src/tools/clippy/tests/ui/write_literal.rs
index 588e8fd41..79d6daa2e 100644
--- a/src/tools/clippy/tests/ui/write_literal.rs
+++ b/src/tools/clippy/tests/ui/write_literal.rs
@@ -43,16 +43,22 @@ fn main() {
// throw a warning
writeln!(v, "{0} {1}", "hello", "world");
//~^ ERROR: literal with an empty format string
- //~| ERROR: literal with an empty format string
writeln!(v, "{1} {0}", "hello", "world");
//~^ ERROR: literal with an empty format string
- //~| ERROR: literal with an empty format string
// named args shouldn't change anything either
writeln!(v, "{foo} {bar}", foo = "hello", bar = "world");
//~^ ERROR: literal with an empty format string
- //~| ERROR: literal with an empty format string
writeln!(v, "{bar} {foo}", foo = "hello", bar = "world");
//~^ ERROR: literal with an empty format string
- //~| ERROR: literal with an empty format string
+
+ // #10128
+ writeln!(v, "{0} {1} {2}", "hello", 2, "world");
+ //~^ ERROR: literal with an empty format string
+ writeln!(v, "{2} {1} {0}", "hello", 2, "world");
+ //~^ ERROR: literal with an empty format string
+ writeln!(v, "{0} {1} {2}, {bar}", "hello", 2, 3, bar = 4);
+ //~^ ERROR: literal with an empty format string
+ writeln!(v, "{0} {1} {2}, {3} {4}", "hello", 2, 3, "world", 4);
+ //~^ ERROR: literal with an empty format string
}
diff --git a/src/tools/clippy/tests/ui/write_literal.stderr b/src/tools/clippy/tests/ui/write_literal.stderr
index 372a54cf7..ee0d536e9 100644
--- a/src/tools/clippy/tests/ui/write_literal.stderr
+++ b/src/tools/clippy/tests/ui/write_literal.stderr
@@ -52,96 +52,96 @@ error: literal with an empty format string
--> $DIR/write_literal.rs:44:28
|
LL | writeln!(v, "{0} {1}", "hello", "world");
- | ^^^^^^^
+ | ^^^^^^^^^^^^^^^^
|
help: try
|
LL - writeln!(v, "{0} {1}", "hello", "world");
-LL + writeln!(v, "hello {1}", "world");
+LL + writeln!(v, "hello world");
|
error: literal with an empty format string
- --> $DIR/write_literal.rs:44:37
+ --> $DIR/write_literal.rs:46:28
|
-LL | writeln!(v, "{0} {1}", "hello", "world");
- | ^^^^^^^
+LL | writeln!(v, "{1} {0}", "hello", "world");
+ | ^^^^^^^^^^^^^^^^
|
help: try
|
-LL - writeln!(v, "{0} {1}", "hello", "world");
-LL + writeln!(v, "{0} world", "hello");
+LL - writeln!(v, "{1} {0}", "hello", "world");
+LL + writeln!(v, "world hello");
|
error: literal with an empty format string
- --> $DIR/write_literal.rs:47:37
+ --> $DIR/write_literal.rs:50:38
|
-LL | writeln!(v, "{1} {0}", "hello", "world");
- | ^^^^^^^
+LL | writeln!(v, "{foo} {bar}", foo = "hello", bar = "world");
+ | ^^^^^^^^^^^^^^^^^^^^^^
|
help: try
|
-LL - writeln!(v, "{1} {0}", "hello", "world");
-LL + writeln!(v, "world {0}", "hello");
+LL - writeln!(v, "{foo} {bar}", foo = "hello", bar = "world");
+LL + writeln!(v, "hello world");
|
error: literal with an empty format string
- --> $DIR/write_literal.rs:47:28
+ --> $DIR/write_literal.rs:52:38
|
-LL | writeln!(v, "{1} {0}", "hello", "world");
- | ^^^^^^^
+LL | writeln!(v, "{bar} {foo}", foo = "hello", bar = "world");
+ | ^^^^^^^^^^^^^^^^^^^^^^
|
help: try
|
-LL - writeln!(v, "{1} {0}", "hello", "world");
-LL + writeln!(v, "{1} hello", "world");
+LL - writeln!(v, "{bar} {foo}", foo = "hello", bar = "world");
+LL + writeln!(v, "world hello");
|
error: literal with an empty format string
- --> $DIR/write_literal.rs:52:38
+ --> $DIR/write_literal.rs:56:32
|
-LL | writeln!(v, "{foo} {bar}", foo = "hello", bar = "world");
- | ^^^^^^^
+LL | writeln!(v, "{0} {1} {2}", "hello", 2, "world");
+ | ^^^^^^^^^^^^^^^^^^^
|
help: try
|
-LL - writeln!(v, "{foo} {bar}", foo = "hello", bar = "world");
-LL + writeln!(v, "hello {bar}", bar = "world");
+LL - writeln!(v, "{0} {1} {2}", "hello", 2, "world");
+LL + writeln!(v, "hello {0} world", 2);
|
error: literal with an empty format string
- --> $DIR/write_literal.rs:52:53
+ --> $DIR/write_literal.rs:58:32
|
-LL | writeln!(v, "{foo} {bar}", foo = "hello", bar = "world");
- | ^^^^^^^
+LL | writeln!(v, "{2} {1} {0}", "hello", 2, "world");
+ | ^^^^^^^^^^^^^^^^^^^
|
help: try
|
-LL - writeln!(v, "{foo} {bar}", foo = "hello", bar = "world");
-LL + writeln!(v, "{foo} world", foo = "hello");
+LL - writeln!(v, "{2} {1} {0}", "hello", 2, "world");
+LL + writeln!(v, "world {0} hello", 2);
|
error: literal with an empty format string
- --> $DIR/write_literal.rs:55:53
+ --> $DIR/write_literal.rs:60:39
|
-LL | writeln!(v, "{bar} {foo}", foo = "hello", bar = "world");
- | ^^^^^^^
+LL | writeln!(v, "{0} {1} {2}, {bar}", "hello", 2, 3, bar = 4);
+ | ^^^^^^^
|
help: try
|
-LL - writeln!(v, "{bar} {foo}", foo = "hello", bar = "world");
-LL + writeln!(v, "world {foo}", foo = "hello");
+LL - writeln!(v, "{0} {1} {2}, {bar}", "hello", 2, 3, bar = 4);
+LL + writeln!(v, "hello {0} {1}, {bar}", 2, 3, bar = 4);
|
error: literal with an empty format string
- --> $DIR/write_literal.rs:55:38
+ --> $DIR/write_literal.rs:62:41
|
-LL | writeln!(v, "{bar} {foo}", foo = "hello", bar = "world");
- | ^^^^^^^
+LL | writeln!(v, "{0} {1} {2}, {3} {4}", "hello", 2, 3, "world", 4);
+ | ^^^^^^^^^^^^^^^^^^^^^^
|
help: try
|
-LL - writeln!(v, "{bar} {foo}", foo = "hello", bar = "world");
-LL + writeln!(v, "{bar} hello", bar = "world");
+LL - writeln!(v, "{0} {1} {2}, {3} {4}", "hello", 2, 3, "world", 4);
+LL + writeln!(v, "hello {0} {1}, world {2}", 2, 3, 4);
|
error: aborting due to 12 previous errors
diff --git a/src/tools/clippy/tests/ui/write_literal_2.rs b/src/tools/clippy/tests/ui/write_literal_2.rs
index aa0c13c13..b2ed552d4 100644
--- a/src/tools/clippy/tests/ui/write_literal_2.rs
+++ b/src/tools/clippy/tests/ui/write_literal_2.rs
@@ -1,6 +1,6 @@
//@no-rustfix: overlapping suggestions
#![allow(unused_must_use)]
-#![warn(clippy::needless_raw_strings, clippy::write_literal)]
+#![warn(clippy::write_literal)]
use std::io::Write;
@@ -11,9 +11,7 @@ fn main() {
//~^ ERROR: literal with an empty format string
//~| NOTE: `-D clippy::write-literal` implied by `-D warnings`
writeln!(v, r"{}", r"{hello}");
- //~^ ERROR: unnecessary raw string literal
- //~| NOTE: `-D clippy::needless-raw-strings` implied by `-D warnings`
- //~| ERROR: literal with an empty format string
+ //~^ ERROR: literal with an empty format string
writeln!(v, "{}", '\'');
//~^ ERROR: literal with an empty format string
writeln!(v, "{}", '"');
@@ -26,17 +24,14 @@ fn main() {
v,
"some {}",
"hello \
- //~^ ERROR: literal with an empty format string
- world!"
+ world!",
+ //~^^ ERROR: literal with an empty format string
);
writeln!(
v,
"some {}\
{} \\ {}",
- "1",
- "2",
- "3",
- //~^ ERROR: literal with an empty format string
+ "1", "2", "3",
);
writeln!(v, "{}", "\\");
//~^ ERROR: literal with an empty format string
@@ -51,7 +46,6 @@ fn main() {
// hard mode
writeln!(v, r#"{}{}"#, '#', '"');
//~^ ERROR: literal with an empty format string
- //~| ERROR: literal with an empty format string
// should not lint
writeln!(v, r"{}", "\r");
}
diff --git a/src/tools/clippy/tests/ui/write_literal_2.stderr b/src/tools/clippy/tests/ui/write_literal_2.stderr
index 6d382a267..81ef49de0 100644
--- a/src/tools/clippy/tests/ui/write_literal_2.stderr
+++ b/src/tools/clippy/tests/ui/write_literal_2.stderr
@@ -1,14 +1,3 @@
-error: unnecessary raw string literal
- --> $DIR/write_literal_2.rs:13:24
- |
-LL | writeln!(v, r"{}", r"{hello}");
- | -^^^^^^^^^
- | |
- | help: use a string literal instead: `"{hello}"`
- |
- = note: `-D clippy::needless-raw-strings` implied by `-D warnings`
- = help: to override `-D warnings` add `#[allow(clippy::needless_raw_strings)]`
-
error: literal with an empty format string
--> $DIR/write_literal_2.rs:10:23
|
@@ -36,7 +25,7 @@ LL + writeln!(v, r"{{hello}}");
|
error: literal with an empty format string
- --> $DIR/write_literal_2.rs:17:23
+ --> $DIR/write_literal_2.rs:15:23
|
LL | writeln!(v, "{}", '\'');
| ^^^^
@@ -48,7 +37,7 @@ LL + writeln!(v, "'");
|
error: literal with an empty format string
- --> $DIR/write_literal_2.rs:19:23
+ --> $DIR/write_literal_2.rs:17:23
|
LL | writeln!(v, "{}", '"');
| ^^^
@@ -60,13 +49,13 @@ LL + writeln!(v, "\"");
|
error: literal with an empty format string
- --> $DIR/write_literal_2.rs:21:24
+ --> $DIR/write_literal_2.rs:19:24
|
LL | writeln!(v, r"{}", '"');
| ^^^
error: literal with an empty format string
- --> $DIR/write_literal_2.rs:23:24
+ --> $DIR/write_literal_2.rs:21:24
|
LL | writeln!(v, r"{}", '\'');
| ^^^^
@@ -78,59 +67,32 @@ LL + writeln!(v, r"'");
|
error: literal with an empty format string
- --> $DIR/write_literal_2.rs:28:9
+ --> $DIR/write_literal_2.rs:26:9
|
LL | / "hello \
-LL | |
-LL | | world!"
+LL | | world!",
| |_______________^
|
help: try
|
LL ~ "some hello \
-LL +
-LL ~ world!"
+LL ~ world!",
|
error: literal with an empty format string
- --> $DIR/write_literal_2.rs:36:9
+ --> $DIR/write_literal_2.rs:34:9
|
-LL | "1",
- | ^^^
+LL | "1", "2", "3",
+ | ^^^^^^^^^^^^^
|
help: try
|
LL ~ "some 1\
-LL ~ {} \\ {}",
+LL ~ 2 \\ 3",
|
error: literal with an empty format string
- --> $DIR/write_literal_2.rs:37:9
- |
-LL | "2",
- | ^^^
- |
-help: try
- |
-LL ~ 2 \\ {}",
-LL ~ "1",
- |
-
-error: literal with an empty format string
- --> $DIR/write_literal_2.rs:38:9
- |
-LL | "3",
- | ^^^
- |
-help: try
- |
-LL ~ {} \\ 3",
-LL | "1",
-LL ~ "2",
- |
-
-error: literal with an empty format string
- --> $DIR/write_literal_2.rs:41:23
+ --> $DIR/write_literal_2.rs:36:23
|
LL | writeln!(v, "{}", "\\");
| ^^^^
@@ -142,7 +104,7 @@ LL + writeln!(v, "\\");
|
error: literal with an empty format string
- --> $DIR/write_literal_2.rs:43:24
+ --> $DIR/write_literal_2.rs:38:24
|
LL | writeln!(v, r"{}", "\\");
| ^^^^
@@ -154,7 +116,7 @@ LL + writeln!(v, r"\");
|
error: literal with an empty format string
- --> $DIR/write_literal_2.rs:45:26
+ --> $DIR/write_literal_2.rs:40:26
|
LL | writeln!(v, r#"{}"#, "\\");
| ^^^^
@@ -166,7 +128,7 @@ LL + writeln!(v, r#"\"#);
|
error: literal with an empty format string
- --> $DIR/write_literal_2.rs:47:23
+ --> $DIR/write_literal_2.rs:42:23
|
LL | writeln!(v, "{}", r"\");
| ^^^^
@@ -178,7 +140,7 @@ LL + writeln!(v, "\\");
|
error: literal with an empty format string
- --> $DIR/write_literal_2.rs:49:23
+ --> $DIR/write_literal_2.rs:44:23
|
LL | writeln!(v, "{}", "\r");
| ^^^^
@@ -190,16 +152,10 @@ LL + writeln!(v, "\r");
|
error: literal with an empty format string
- --> $DIR/write_literal_2.rs:52:28
- |
-LL | writeln!(v, r#"{}{}"#, '#', '"');
- | ^^^
-
-error: literal with an empty format string
- --> $DIR/write_literal_2.rs:52:33
+ --> $DIR/write_literal_2.rs:47:28
|
LL | writeln!(v, r#"{}{}"#, '#', '"');
- | ^^^
+ | ^^^^^^^^
-error: aborting due to 18 previous errors
+error: aborting due to 14 previous errors
diff --git a/src/tools/clippy/tests/versioncheck.rs b/src/tools/clippy/tests/versioncheck.rs
index c721e9969..eba5405e6 100644
--- a/src/tools/clippy/tests/versioncheck.rs
+++ b/src/tools/clippy/tests/versioncheck.rs
@@ -26,6 +26,7 @@ fn consistent_clippy_crate_versions() {
let paths = [
"declare_clippy_lint/Cargo.toml",
+ "clippy_config/Cargo.toml",
"clippy_lints/Cargo.toml",
"clippy_utils/Cargo.toml",
];
diff --git a/src/tools/clippy/triagebot.toml b/src/tools/clippy/triagebot.toml
index 6856bb0ab..419b3c30d 100644
--- a/src/tools/clippy/triagebot.toml
+++ b/src/tools/clippy/triagebot.toml
@@ -11,6 +11,8 @@ allow-unauthenticated = [
# Have rustbot inform users about the *No Merge Policy*
[no-merges]
+exclude_titles = ["Rustup"] # exclude syncs from rust-lang/rust
+labels = ["has-merge-commits", "S-waiting-on-author"]
[autolabel."S-waiting-on-review"]
new_pr = true
diff --git a/src/tools/compiletest/Cargo.toml b/src/tools/compiletest/Cargo.toml
index bb1fa6e92..31c6353e6 100644
--- a/src/tools/compiletest/Cargo.toml
+++ b/src/tools/compiletest/Cargo.toml
@@ -11,6 +11,7 @@ colored = "2"
diff = "0.1.10"
unified-diff = "0.2.1"
getopts = "0.2"
+indexmap = "2.0.0"
miropt-test-tools = { path = "../miropt-test-tools" }
build_helper = { path = "../build_helper" }
tracing = "0.1"
diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs
index 0e1bf0c6c..1e9684555 100644
--- a/src/tools/compiletest/src/common.rs
+++ b/src/tools/compiletest/src/common.rs
@@ -8,6 +8,7 @@ use std::process::Command;
use std::str::FromStr;
use crate::util::{add_dylib_path, PathBufExt};
+use build_helper::git::GitConfig;
use lazycell::AtomicLazyCell;
use serde::de::{Deserialize, Deserializer, Error as _};
use std::collections::{HashMap, HashSet};
@@ -67,7 +68,7 @@ string_enum! {
MirOpt => "mir-opt",
Assembly => "assembly",
CoverageMap => "coverage-map",
- RunCoverage => "run-coverage",
+ CoverageRun => "coverage-run",
}
}
@@ -78,7 +79,7 @@ impl Default for Mode {
}
impl Mode {
- pub fn disambiguator(self) -> &'static str {
+ pub fn aux_dir_disambiguator(self) -> &'static str {
// Pretty-printing tests could run concurrently, and if they do,
// they need to keep their output segregated.
match self {
@@ -86,6 +87,15 @@ impl Mode {
_ => "",
}
}
+
+ pub fn output_dir_disambiguator(self) -> &'static str {
+ // Coverage tests use the same test files for multiple test modes,
+ // so each mode should have a separate output directory.
+ match self {
+ CoverageMap | CoverageRun => self.to_str(),
+ _ => "",
+ }
+ }
}
string_enum! {
@@ -370,6 +380,10 @@ pub struct Config {
pub target_cfgs: AtomicLazyCell<TargetCfgs>,
pub nocapture: bool,
+
+ // Needed both to construct build_helper::git::GitConfig
+ pub git_repository: String,
+ pub nightly_branch: String,
}
impl Config {
@@ -441,6 +455,10 @@ impl Config {
];
ASM_SUPPORTED_ARCHS.contains(&self.target_cfg().arch.as_str())
}
+
+ pub fn git_config(&self) -> GitConfig<'_> {
+ GitConfig { git_repository: &self.git_repository, nightly_branch: &self.nightly_branch }
+ }
}
#[derive(Debug, Clone)]
@@ -699,6 +717,7 @@ pub fn output_testname_unique(
let mode = config.compare_mode.as_ref().map_or("", |m| m.to_str());
let debugger = config.debugger.as_ref().map_or("", |m| m.to_str());
PathBuf::from(&testpaths.file.file_stem().unwrap())
+ .with_extra_extension(config.mode.output_dir_disambiguator())
.with_extra_extension(revision.unwrap_or(""))
.with_extra_extension(mode)
.with_extra_extension(debugger)
diff --git a/src/tools/compiletest/src/header.rs b/src/tools/compiletest/src/header.rs
index 269d93843..d6516cff6 100644
--- a/src/tools/compiletest/src/header.rs
+++ b/src/tools/compiletest/src/header.rs
@@ -322,7 +322,15 @@ impl TestProps {
);
if let Some(flags) = config.parse_name_value_directive(ln, COMPILE_FLAGS) {
- self.compile_flags.extend(flags.split_whitespace().map(|s| s.to_owned()));
+ self.compile_flags.extend(
+ flags
+ .split("'")
+ .enumerate()
+ .flat_map(|(i, f)| {
+ if i % 2 == 1 { vec![f] } else { f.split_whitespace().collect() }
+ })
+ .map(|s| s.to_owned()),
+ );
}
if config.parse_name_value_directive(ln, INCORRECT_COMPILER_FLAGS).is_some() {
panic!("`compiler-flags` directive should be spelled `compile-flags`");
@@ -903,11 +911,11 @@ pub fn make_test_description<R: Read>(
let mut should_fail = false;
let extra_directives: &[&str] = match config.mode {
- // The run-coverage tests are treated as having these extra directives,
+ // The coverage-run tests are treated as having these extra directives,
// without needing to specify them manually in every test file.
// (Some of the comments below have been copied over from
// `tests/run-make/coverage-reports/Makefile`, which no longer exists.)
- Mode::RunCoverage => {
+ Mode::CoverageRun => {
&[
"needs-profiler-support",
// FIXME(mati865): MinGW GCC miscompiles compiler-rt profiling library but with Clang it works
diff --git a/src/tools/compiletest/src/header/needs.rs b/src/tools/compiletest/src/header/needs.rs
index 2b7a4387c..4a40fb55f 100644
--- a/src/tools/compiletest/src/header/needs.rs
+++ b/src/tools/compiletest/src/header/needs.rs
@@ -241,8 +241,8 @@ impl CachedNeedsConditions {
profiler_support: std::env::var_os("RUSTC_PROFILER_SUPPORT").is_some(),
xray: config.target_cfg().xray,
- // For tests using the `needs-rust-lld` directive (e.g. for `-Zgcc-ld=lld`), we need to find
- // whether `rust-lld` is present in the compiler under test.
+ // For tests using the `needs-rust-lld` directive (e.g. for `-Clink-self-contained=+linker`),
+ // we need to find whether `rust-lld` is present in the compiler under test.
//
// The --compile-lib-path is the path to host shared libraries, but depends on the OS. For
// example:
diff --git a/src/tools/compiletest/src/header/tests.rs b/src/tools/compiletest/src/header/tests.rs
index 2fd80b52c..85e745bed 100644
--- a/src/tools/compiletest/src/header/tests.rs
+++ b/src/tools/compiletest/src/header/tests.rs
@@ -126,6 +126,8 @@ impl ConfigBuilder {
self.host.as_deref().unwrap_or("x86_64-unknown-linux-gnu"),
"--target",
self.target.as_deref().unwrap_or("x86_64-unknown-linux-gnu"),
+ "--git-repository=",
+ "--nightly-branch=",
];
let mut args: Vec<String> = args.iter().map(ToString::to_string).collect();
diff --git a/src/tools/compiletest/src/lib.rs b/src/tools/compiletest/src/lib.rs
index 619ff9b32..bb09c03ef 100644
--- a/src/tools/compiletest/src/lib.rs
+++ b/src/tools/compiletest/src/lib.rs
@@ -144,7 +144,9 @@ pub fn parse_config(args: Vec<String>) -> Config {
.optflag("h", "help", "show this message")
.reqopt("", "channel", "current Rust channel", "CHANNEL")
.optflag("", "git-hash", "run tests which rely on commit version being compiled into the binaries")
- .optopt("", "edition", "default Rust edition", "EDITION");
+ .optopt("", "edition", "default Rust edition", "EDITION")
+ .reqopt("", "git-repository", "name of the git repository", "ORG/REPO")
+ .reqopt("", "nightly-branch", "name of the git branch for nightly", "BRANCH");
let (argv0, args_) = args.split_first().unwrap();
if args.len() == 1 || args[1] == "-h" || args[1] == "--help" {
@@ -307,6 +309,9 @@ pub fn parse_config(args: Vec<String>) -> Config {
target_cfgs: AtomicLazyCell::new(),
nocapture: matches.opt_present("nocapture"),
+
+ git_repository: matches.opt_str("git-repository").unwrap(),
+ nightly_branch: matches.opt_str("nightly-branch").unwrap(),
}
}
@@ -609,9 +614,10 @@ fn modified_tests(config: &Config, dir: &Path) -> Result<Vec<PathBuf>, String> {
return Ok(vec![]);
}
let files =
- get_git_modified_files(Some(dir), &vec!["rs", "stderr", "fixed"])?.unwrap_or(vec![]);
+ get_git_modified_files(&config.git_config(), Some(dir), &vec!["rs", "stderr", "fixed"])?
+ .unwrap_or(vec![]);
// Add new test cases to the list, it will be convenient in daily development.
- let untracked_files = get_git_untracked_files(None)?.unwrap_or(vec![]);
+ let untracked_files = get_git_untracked_files(&config.git_config(), None)?.unwrap_or(vec![]);
let all_paths = [&files[..], &untracked_files[..]].concat();
let full_paths = {
diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs
index 657d074b3..63e8ba7c7 100644
--- a/src/tools/compiletest/src/runtest.rs
+++ b/src/tools/compiletest/src/runtest.rs
@@ -6,7 +6,7 @@ use crate::common::{Assembly, Incremental, JsDocTest, MirOpt, RunMake, RustdocJs
use crate::common::{Codegen, CodegenUnits, DebugInfo, Debugger, Rustdoc};
use crate::common::{CompareMode, FailMode, PassMode};
use crate::common::{Config, TestPaths};
-use crate::common::{CoverageMap, Pretty, RunCoverage, RunPassValgrind};
+use crate::common::{CoverageMap, CoverageRun, Pretty, RunPassValgrind};
use crate::common::{UI_COVERAGE, UI_COVERAGE_MAP, UI_RUN_STDERR, UI_RUN_STDOUT};
use crate::compute_diff::{write_diff, write_filtered_diff};
use crate::errors::{self, Error, ErrorKind};
@@ -15,6 +15,7 @@ use crate::json;
use crate::read2::{read2_abbreviated, Truncated};
use crate::util::{add_dylib_path, dylib_env_var, logv, PathBufExt};
use crate::ColorConfig;
+use miropt_test_tools::{files_for_miropt_test, MiroptTest, MiroptTestFile};
use regex::{Captures, Regex};
use rustfix::{apply_suggestions, get_suggestions_from_json, Filter};
@@ -229,6 +230,7 @@ enum Emit {
None,
Metadata,
LlvmIr,
+ Mir,
Asm,
LinkArgsAsm,
}
@@ -255,7 +257,7 @@ impl<'test> TestCx<'test> {
Assembly => self.run_assembly_test(),
JsDocTest => self.run_js_doc_test(),
CoverageMap => self.run_coverage_map_test(),
- RunCoverage => self.run_coverage_test(),
+ CoverageRun => self.run_coverage_run_test(),
}
}
@@ -508,7 +510,7 @@ impl<'test> TestCx<'test> {
}
}
- fn run_coverage_test(&self) {
+ fn run_coverage_run_test(&self) {
let should_run = self.run_if_enabled();
let proc_res = self.compile_test(should_run, Emit::None);
@@ -547,7 +549,7 @@ impl<'test> TestCx<'test> {
let mut profraw_paths = vec![profraw_path];
let mut bin_paths = vec![self.make_exe_name()];
- if self.config.suite == "run-coverage-rustdoc" {
+ if self.config.suite == "coverage-run-rustdoc" {
self.run_doctests_for_coverage(&mut profraw_paths, &mut bin_paths);
}
@@ -2191,7 +2193,7 @@ impl<'test> TestCx<'test> {
|| self.is_vxworks_pure_static()
|| self.config.target.contains("bpf")
|| !self.config.target_cfg().dynamic_linking
- || self.config.mode == RunCoverage
+ || matches!(self.config.mode, CoverageMap | CoverageRun)
{
// We primarily compile all auxiliary libraries as dynamic libraries
// to avoid code size bloat and large binaries as much as possible
@@ -2335,17 +2337,14 @@ impl<'test> TestCx<'test> {
rustc.arg("-Zsimulate-remapped-rust-src-base=/rustc/FAKE_PREFIX");
rustc.arg("-Ztranslate-remapped-path-to-local-path=no");
- // #[cfg(not(bootstrap))]: After beta bump, this should **always** run.
- if !(self.config.stage_id.starts_with("stage1-") && self.config.suite == "ui-fulldeps") {
- // Hide Cargo dependency sources from ui tests to make sure the error message doesn't
- // change depending on whether $CARGO_HOME is remapped or not. If this is not present,
- // when $CARGO_HOME is remapped the source won't be shown, and when it's not remapped the
- // source will be shown, causing a blessing hell.
- rustc.arg("-Z").arg(format!(
- "ignore-directory-in-diagnostics-source-blocks={}",
- home::cargo_home().expect("failed to find cargo home").to_str().unwrap()
- ));
- }
+ // Hide Cargo dependency sources from ui tests to make sure the error message doesn't
+ // change depending on whether $CARGO_HOME is remapped or not. If this is not present,
+ // when $CARGO_HOME is remapped the source won't be shown, and when it's not remapped the
+ // source will be shown, causing a blessing hell.
+ rustc.arg("-Z").arg(format!(
+ "ignore-directory-in-diagnostics-source-blocks={}",
+ home::cargo_home().expect("failed to find cargo home").to_str().unwrap()
+ ));
// Optionally prevent default --sysroot if specified in test compile-flags.
if !self.props.compile_flags.iter().any(|flag| flag.starts_with("--sysroot"))
@@ -2396,7 +2395,7 @@ impl<'test> TestCx<'test> {
}
}
DebugInfo => { /* debuginfo tests must be unoptimized */ }
- CoverageMap | RunCoverage => {
+ CoverageMap | CoverageRun => {
// Coverage mappings and coverage reports are affected by
// optimization level, so they ignore the optimize-tests
// setting and set an optimization level in their mode's
@@ -2471,7 +2470,7 @@ impl<'test> TestCx<'test> {
}
CoverageMap => {
rustc.arg("-Cinstrument-coverage");
- // These tests only compile to MIR, so they don't need the
+ // These tests only compile to LLVM IR, so they don't need the
// profiler runtime to be present.
rustc.arg("-Zno-profiler-runtime");
// Coverage mappings are sensitive to MIR optimizations, and
@@ -2479,12 +2478,12 @@ impl<'test> TestCx<'test> {
// by `compile-flags`.
rustc.arg("-Copt-level=2");
}
- RunCoverage => {
+ CoverageRun => {
rustc.arg("-Cinstrument-coverage");
// Coverage reports are sometimes sensitive to optimizations,
- // and the current snapshots assume no optimization unless
+ // and the current snapshots assume `opt-level=2` unless
// overridden by `compile-flags`.
- rustc.arg("-Copt-level=0");
+ rustc.arg("-Copt-level=2");
}
RunPassValgrind | Pretty | DebugInfo | Codegen | Rustdoc | RustdocJson | RunMake
| CodegenUnits | JsDocTest | Assembly => {
@@ -2509,6 +2508,9 @@ impl<'test> TestCx<'test> {
Emit::LlvmIr => {
rustc.args(&["--emit", "llvm-ir"]);
}
+ Emit::Mir => {
+ rustc.args(&["--emit", "mir"]);
+ }
Emit::Asm => {
rustc.args(&["--emit", "asm"]);
}
@@ -2718,7 +2720,7 @@ impl<'test> TestCx<'test> {
fn aux_output_dir_name(&self) -> PathBuf {
self.output_base_dir()
.join("auxiliary")
- .with_extra_extension(self.config.mode.disambiguator())
+ .with_extra_extension(self.config.mode.aux_dir_disambiguator())
}
/// Generates a unique name for the test, such as `testname.revision.mode`.
@@ -3544,10 +3546,6 @@ impl<'test> TestCx<'test> {
cmd.env("RUSTDOC", cwd.join(rustdoc));
}
- if let Some(ref rust_demangler) = self.config.rust_demangler_path {
- cmd.env("RUST_DEMANGLER", cwd.join(rust_demangler));
- }
-
if let Some(ref node) = self.config.nodejs {
cmd.env("NODE", node);
}
@@ -3964,7 +3962,7 @@ impl<'test> TestCx<'test> {
// And finally, compile the fixed code and make sure it both
// succeeds and has no diagnostics.
let rustc = self.make_compile_args(
- &self.testpaths.file.with_extension(UI_FIXED),
+ &self.expected_output_path(UI_FIXED),
TargetLocation::ThisFile(self.make_exe_name()),
emit_metadata,
AllowUnused::No,
@@ -3987,14 +3985,20 @@ impl<'test> TestCx<'test> {
fn run_mir_opt_test(&self) {
let pm = self.pass_mode();
let should_run = self.should_run(pm);
- let emit_metadata = self.should_emit_metadata(pm);
- let passes = self.get_passes();
- let proc_res = self.compile_test_with_passes(should_run, emit_metadata, passes);
- self.check_mir_dump();
+ let mut test_info = files_for_miropt_test(
+ &self.testpaths.file,
+ self.config.get_pointer_width(),
+ self.config.target_cfg().panic.for_miropt_test_tools(),
+ );
+
+ let passes = std::mem::take(&mut test_info.passes);
+
+ let proc_res = self.compile_test_with_passes(should_run, Emit::Mir, passes);
if !proc_res.status.success() {
self.fatal_proc_rec("compilation failed!", &proc_res);
}
+ self.check_mir_dump(test_info);
if let WillExecute::Yes = should_run {
let proc_res = self.exec_compiled_test();
@@ -4005,37 +4009,12 @@ impl<'test> TestCx<'test> {
}
}
- fn get_passes(&self) -> Vec<String> {
- let files = miropt_test_tools::files_for_miropt_test(
- &self.testpaths.file,
- self.config.get_pointer_width(),
- self.config.target_cfg().panic.for_miropt_test_tools(),
- );
-
- let mut out = Vec::new();
-
- for miropt_test_tools::MiroptTestFiles {
- from_file: _,
- to_file: _,
- expected_file: _,
- passes,
- } in files
- {
- out.extend(passes);
- }
- out
- }
-
- fn check_mir_dump(&self) {
+ fn check_mir_dump(&self, test_info: MiroptTest) {
let test_dir = self.testpaths.file.parent().unwrap();
let test_crate =
self.testpaths.file.file_stem().unwrap().to_str().unwrap().replace("-", "_");
- let suffix = miropt_test_tools::output_file_suffix(
- &self.testpaths.file,
- self.config.get_pointer_width(),
- self.config.target_cfg().panic.for_miropt_test_tools(),
- );
+ let MiroptTest { run_filecheck, suffix, files, passes: _ } = test_info;
if self.config.bless {
for e in
@@ -4050,14 +4029,7 @@ impl<'test> TestCx<'test> {
}
}
- let files = miropt_test_tools::files_for_miropt_test(
- &self.testpaths.file,
- self.config.get_pointer_width(),
- self.config.target_cfg().panic.for_miropt_test_tools(),
- );
- for miropt_test_tools::MiroptTestFiles { from_file, to_file, expected_file, passes: _ } in
- files
- {
+ for MiroptTestFile { from_file, to_file, expected_file } in files {
let dumped_string = if let Some(after) = to_file {
self.diff_mir_files(from_file.into(), after.into())
} else {
@@ -4098,6 +4070,14 @@ impl<'test> TestCx<'test> {
}
}
}
+
+ if run_filecheck {
+ let output_path = self.output_base_name().with_extension("mir");
+ let proc_res = self.verify_with_filecheck(&output_path);
+ if !proc_res.status.success() {
+ self.fatal_proc_rec("verification with 'FileCheck' failed", &proc_res);
+ }
+ }
}
fn diff_mir_files(&self, before: PathBuf, after: PathBuf) -> String {
@@ -4261,6 +4241,39 @@ impl<'test> TestCx<'test> {
V0_BACK_REF_RE.replace_all(&normalized, V0_BACK_REF_PLACEHOLDER).into_owned();
}
+ // AllocId are numbered globally in a compilation session. This can lead to changes
+ // depending on the exact compilation flags and host architecture. Meanwhile, we want
+ // to keep them numbered, to see if the same id appears multiple times.
+ // So we remap to deterministic numbers that only depend on the subset of allocations
+ // that actually appear in the output.
+ // We use uppercase ALLOC to distinguish from the non-normalized version.
+ {
+ let mut seen_allocs = indexmap::IndexSet::new();
+
+ // The alloc-id appears in pretty-printed allocations.
+ let re = Regex::new(r"╾─*a(lloc)?([0-9]+)(\+0x[0-9]+)?─*╼").unwrap();
+ normalized = re
+ .replace_all(&normalized, |caps: &Captures<'_>| {
+ // Renumber the captured index.
+ let index = caps.get(2).unwrap().as_str().to_string();
+ let (index, _) = seen_allocs.insert_full(index);
+ let offset = caps.get(3).map_or("", |c| c.as_str());
+ // Do not bother keeping it pretty, just make it deterministic.
+ format!("╾ALLOC{index}{offset}╼")
+ })
+ .into_owned();
+
+ // The alloc-id appears in a sentence.
+ let re = Regex::new(r"\balloc([0-9]+)\b").unwrap();
+ normalized = re
+ .replace_all(&normalized, |caps: &Captures<'_>| {
+ let index = caps.get(1).unwrap().as_str().to_string();
+ let (index, _) = seen_allocs.insert_full(index);
+ format!("ALLOC{index}")
+ })
+ .into_owned();
+ }
+
// Custom normalization rules
for rule in custom_rules {
let re = Regex::new(&rule.0).expect("bad regex in custom normalization rule");
diff --git a/src/tools/lld-wrapper/src/main.rs b/src/tools/lld-wrapper/src/main.rs
index b5e977b26..da94e686f 100644
--- a/src/tools/lld-wrapper/src/main.rs
+++ b/src/tools/lld-wrapper/src/main.rs
@@ -4,8 +4,8 @@
//! two arguments the `<flavor>` command line interface is used to process the remaining arguments.
//! If no `-flavor` argument is present the flavor is determined by the executable name.
//!
-//! In Rust with `-Z gcc-ld=lld` we have gcc or clang invoke rust-lld. Since there is no way to
-//! make gcc/clang pass `-flavor <flavor>` as the first two arguments in the linker invocation
+//! With `-Clink-self-contained=+linker` we have gcc or clang invoke rust-lld. Since there is no way
+//! to make gcc/clang pass `-flavor <flavor>` as the first two arguments in the linker invocation
//! and since Windows does not support symbolic links for files this wrapper is used in place of a
//! symbolic link. It execs `../rust-lld -flavor <flavor>` by propagating the flavor argument
//! obtained from the wrapper's name as the first two arguments.
diff --git a/src/tools/miropt-test-tools/src/lib.rs b/src/tools/miropt-test-tools/src/lib.rs
index e33ecfe8e..cae96f593 100644
--- a/src/tools/miropt-test-tools/src/lib.rs
+++ b/src/tools/miropt-test-tools/src/lib.rs
@@ -1,10 +1,16 @@
use std::fs;
use std::path::Path;
-pub struct MiroptTestFiles {
+pub struct MiroptTestFile {
pub expected_file: std::path::PathBuf,
pub from_file: String,
pub to_file: Option<String>,
+}
+
+pub struct MiroptTest {
+ pub run_filecheck: bool,
+ pub suffix: String,
+ pub files: Vec<MiroptTestFile>,
/// Vec of passes under test to be dumped
pub passes: Vec<String>,
}
@@ -14,11 +20,7 @@ pub enum PanicStrategy {
Abort,
}
-pub fn output_file_suffix(
- testfile: &Path,
- bit_width: u32,
- panic_strategy: PanicStrategy,
-) -> String {
+fn output_file_suffix(testfile: &Path, bit_width: u32, panic_strategy: PanicStrategy) -> String {
let mut each_bit_width = false;
let mut each_panic_strategy = false;
for line in fs::read_to_string(testfile).unwrap().lines() {
@@ -47,7 +49,7 @@ pub fn files_for_miropt_test(
testfile: &std::path::Path,
bit_width: u32,
panic_strategy: PanicStrategy,
-) -> Vec<MiroptTestFiles> {
+) -> MiroptTest {
let mut out = Vec::new();
let test_file_contents = fs::read_to_string(&testfile).unwrap();
@@ -55,8 +57,14 @@ pub fn files_for_miropt_test(
let test_crate = testfile.file_stem().unwrap().to_str().unwrap().replace('-', "_");
let suffix = output_file_suffix(testfile, bit_width, panic_strategy);
+ let mut run_filecheck = true;
+ let mut passes = Vec::new();
for l in test_file_contents.lines() {
+ if l.starts_with("// skip-filecheck") {
+ run_filecheck = false;
+ continue;
+ }
if l.starts_with("// EMIT_MIR ") {
let test_name = l.trim_start_matches("// EMIT_MIR ").trim();
let mut test_names = test_name.split(' ');
@@ -65,7 +73,6 @@ pub fn files_for_miropt_test(
let mut expected_file;
let from_file;
let to_file;
- let mut passes = Vec::new();
if test_name.ends_with(".diff") {
let trimmed = test_name.trim_end_matches(".diff");
@@ -114,9 +121,9 @@ pub fn files_for_miropt_test(
}
let expected_file = test_dir.join(expected_file);
- out.push(MiroptTestFiles { expected_file, from_file, to_file, passes });
+ out.push(MiroptTestFile { expected_file, from_file, to_file });
}
}
- out
+ MiroptTest { run_filecheck, suffix, files: out, passes }
}
diff --git a/src/tools/opt-dist/Cargo.toml b/src/tools/opt-dist/Cargo.toml
index f1c3dd6aa..9e852b064 100644
--- a/src/tools/opt-dist/Cargo.toml
+++ b/src/tools/opt-dist/Cargo.toml
@@ -23,3 +23,4 @@ glob = "0.3"
tempfile = "3.5"
derive_builder = "0.12"
clap = { version = "4", features = ["derive"] }
+tabled = { version = "0.13", default-features = false, features = ["std"] }
diff --git a/src/tools/opt-dist/src/bolt.rs b/src/tools/opt-dist/src/bolt.rs
index cf9f4fabc..f694c08f9 100644
--- a/src/tools/opt-dist/src/bolt.rs
+++ b/src/tools/opt-dist/src/bolt.rs
@@ -1,14 +1,14 @@
use anyhow::Context;
use crate::exec::cmd;
-use crate::training::LlvmBoltProfile;
+use crate::training::BoltProfile;
use camino::{Utf8Path, Utf8PathBuf};
use crate::utils::io::copy_file;
/// Instruments an artifact at the given `path` (in-place) with BOLT and then calls `func`.
/// After this function finishes, the original file will be restored.
-pub fn with_bolt_instrumented<F: FnOnce() -> anyhow::Result<R>, R>(
+pub fn with_bolt_instrumented<F: FnOnce(&Utf8Path) -> anyhow::Result<R>, R>(
path: &Utf8Path,
func: F,
) -> anyhow::Result<R> {
@@ -20,10 +20,16 @@ pub fn with_bolt_instrumented<F: FnOnce() -> anyhow::Result<R>, R>(
let instrumented_path = tempfile::NamedTempFile::new()?.into_temp_path();
+ let profile_dir =
+ tempfile::TempDir::new().context("Could not create directory for BOLT profiles")?;
+ let profile_prefix = profile_dir.path().join("prof.fdata");
+ let profile_prefix = Utf8Path::from_path(&profile_prefix).unwrap();
+
// Instrument the original file with BOLT, saving the result into `instrumented_path`
cmd(&["llvm-bolt"])
.arg("-instrument")
.arg(path)
+ .arg(&format!("--instrumentation-file={profile_prefix}"))
// Make sure that each process will write its profiles into a separate file
.arg("--instrumentation-file-append-pid")
.arg("-o")
@@ -36,11 +42,11 @@ pub fn with_bolt_instrumented<F: FnOnce() -> anyhow::Result<R>, R>(
// Run the function that will make use of the instrumented artifact.
// The original file will be restored when `_backup_file` is dropped.
- func()
+ func(profile_prefix)
}
/// Optimizes the file at `path` with BOLT in-place using the given `profile`.
-pub fn bolt_optimize(path: &Utf8Path, profile: &LlvmBoltProfile) -> anyhow::Result<()> {
+pub fn bolt_optimize(path: &Utf8Path, profile: &BoltProfile) -> anyhow::Result<()> {
// Copy the artifact to a new location, so that we do not use the same input and output file.
// BOLT cannot handle optimizing when the input and output is the same file, because it performs
// in-place patching.
diff --git a/src/tools/opt-dist/src/exec.rs b/src/tools/opt-dist/src/exec.rs
index 04e018452..90a045e83 100644
--- a/src/tools/opt-dist/src/exec.rs
+++ b/src/tools/opt-dist/src/exec.rs
@@ -1,7 +1,7 @@
use crate::environment::Environment;
use crate::metrics::{load_metrics, record_metrics};
use crate::timer::TimerSection;
-use crate::training::{LlvmBoltProfile, LlvmPGOProfile, RustcPGOProfile};
+use crate::training::{BoltProfile, LlvmPGOProfile, RustcPGOProfile};
use camino::{Utf8Path, Utf8PathBuf};
use std::collections::BTreeMap;
use std::fs::File;
@@ -159,7 +159,12 @@ impl Bootstrap {
self
}
- pub fn with_bolt_profile(mut self, profile: LlvmBoltProfile) -> Self {
+ pub fn with_rustc_bolt_ldflags(mut self) -> Self {
+ self.cmd = self.cmd.arg("--enable-bolt-settings");
+ self
+ }
+
+ pub fn with_bolt_profile(mut self, profile: BoltProfile) -> Self {
self.cmd = self.cmd.arg("--reproducible-artifact").arg(profile.0.as_str());
self
}
diff --git a/src/tools/opt-dist/src/main.rs b/src/tools/opt-dist/src/main.rs
index 978e2dfa4..f9ff1a0a4 100644
--- a/src/tools/opt-dist/src/main.rs
+++ b/src/tools/opt-dist/src/main.rs
@@ -12,11 +12,15 @@ use crate::environment::{Environment, EnvironmentBuilder};
use crate::exec::{cmd, Bootstrap};
use crate::tests::run_tests;
use crate::timer::Timer;
-use crate::training::{gather_llvm_bolt_profiles, gather_llvm_profiles, gather_rustc_profiles};
+use crate::training::{
+ gather_bolt_profiles, gather_llvm_profiles, gather_rustc_profiles, llvm_benchmarks,
+ rustc_benchmarks,
+};
+use crate::utils::artifact_size::print_binary_sizes;
use crate::utils::io::{copy_directory, move_directory, reset_directory};
use crate::utils::{
- clear_llvm_files, format_env_variables, print_binary_sizes, print_free_disk_space,
- retry_action, with_log_group,
+ clear_llvm_files, format_env_variables, print_free_disk_space, retry_action, with_log_group,
+ write_timer_to_summary,
};
mod bolt;
@@ -211,7 +215,12 @@ fn execute_pipeline(
print_free_disk_space()?;
stage.section("Build PGO optimized rustc", |section| {
- Bootstrap::build(env).rustc_pgo_optimize(&profile).run(section)
+ let mut cmd = Bootstrap::build(env).rustc_pgo_optimize(&profile);
+ if env.use_bolt() {
+ cmd = cmd.with_rustc_bolt_ldflags();
+ }
+
+ cmd.run(section)
})?;
Ok(profile)
@@ -245,13 +254,13 @@ fn execute_pipeline(
Ok(profile)
})?;
- let llvm_bolt_profile = if env.use_bolt() {
+ let bolt_profiles = if env.use_bolt() {
// Stage 3: Build BOLT instrumented LLVM
// We build a PGO optimized LLVM in this step, then instrument it with BOLT and gather BOLT profiles.
// Note that we don't remove LLVM artifacts after this step, so that they are reused in the final dist build.
// BOLT instrumentation is performed "on-the-fly" when the LLVM library is copied to the sysroot of rustc,
// therefore the LLVM artifacts on disk are not "tainted" with BOLT instrumentation and they can be reused.
- timer.section("Stage 3 (LLVM BOLT)", |stage| {
+ timer.section("Stage 3 (BOLT)", |stage| {
stage.section("Build PGO optimized LLVM", |stage| {
Bootstrap::build(env)
.with_llvm_bolt_ldflags()
@@ -260,16 +269,17 @@ fn execute_pipeline(
.run(stage)
})?;
- // Find the path to the `libLLVM.so` file
- let llvm_lib = io::find_file_in_dir(
- &env.build_artifacts().join("stage2").join("lib"),
- "libLLVM",
- ".so",
- )?;
+ let libdir = env.build_artifacts().join("stage2").join("lib");
+ let llvm_lib = io::find_file_in_dir(&libdir, "libLLVM", ".so")?;
- // Instrument it and gather profiles
- let profile = with_bolt_instrumented(&llvm_lib, || {
- stage.section("Gather profiles", |_| gather_llvm_bolt_profiles(env))
+ log::info!("Optimizing {llvm_lib} with BOLT");
+
+ // FIXME(kobzol): try gather profiles together, at once for LLVM and rustc
+ // Instrument the libraries and gather profiles
+ let llvm_profile = with_bolt_instrumented(&llvm_lib, |llvm_profile_dir| {
+ stage.section("Gather profiles", |_| {
+ gather_bolt_profiles(env, "LLVM", llvm_benchmarks(env), llvm_profile_dir)
+ })
})?;
print_free_disk_space()?;
@@ -278,13 +288,29 @@ fn execute_pipeline(
// the final dist build. However, when BOLT optimizes an artifact, it does so *in-place*,
// therefore it will actually optimize all the hard links, which means that the final
// packaged `libLLVM.so` file *will* be BOLT optimized.
- bolt_optimize(&llvm_lib, &profile).context("Could not optimize LLVM with BOLT")?;
+ bolt_optimize(&llvm_lib, &llvm_profile).context("Could not optimize LLVM with BOLT")?;
+
+ let rustc_lib = io::find_file_in_dir(&libdir, "librustc_driver", ".so")?;
+
+ log::info!("Optimizing {rustc_lib} with BOLT");
+
+ // Instrument it and gather profiles
+ let rustc_profile = with_bolt_instrumented(&rustc_lib, |rustc_profile_dir| {
+ stage.section("Gather profiles", |_| {
+ gather_bolt_profiles(env, "rustc", rustc_benchmarks(env), rustc_profile_dir)
+ })
+ })?;
+ print_free_disk_space()?;
+
+ // Now optimize the library with BOLT.
+ bolt_optimize(&rustc_lib, &rustc_profile)
+ .context("Could not optimize rustc with BOLT")?;
// LLVM is not being cleared here, we want to use the BOLT-optimized LLVM
- Ok(Some(profile))
+ Ok(vec![llvm_profile, rustc_profile])
})?
} else {
- None
+ vec![]
};
let mut dist = Bootstrap::dist(env, &dist_args)
@@ -292,13 +318,13 @@ fn execute_pipeline(
.rustc_pgo_optimize(&rustc_pgo_profile)
.avoid_rustc_rebuild();
- if let Some(llvm_bolt_profile) = llvm_bolt_profile {
- dist = dist.with_bolt_profile(llvm_bolt_profile);
+ for bolt_profile in bolt_profiles {
+ dist = dist.with_bolt_profile(bolt_profile);
}
// Final stage: Assemble the dist artifacts
// The previous PGO optimized rustc build and PGO optimized LLVM builds should be reused.
- timer.section("Stage 4 (final build)", |stage| dist.run(stage))?;
+ timer.section("Stage 5 (final build)", |stage| dist.run(stage))?;
// After dist has finished, run a subset of the test suite on the optimized artifacts to discover
// possible regressions.
@@ -359,6 +385,10 @@ fn main() -> anyhow::Result<()> {
let result = execute_pipeline(&env, &mut timer, build_args);
log::info!("Timer results\n{}", timer.format_stats());
+ if let Ok(summary_path) = std::env::var("GITHUB_STEP_SUMMARY") {
+ write_timer_to_summary(&summary_path, &timer)?;
+ }
+
print_free_disk_space()?;
result.context("Optimized build pipeline has failed")?;
print_binary_sizes(&env)?;
@@ -378,8 +408,8 @@ fn download_rustc_perf(env: &Environment) -> anyhow::Result<()> {
// FIXME: add some mechanism for synchronization of this commit SHA with
// Linux (which builds rustc-perf in a Dockerfile)
- // rustc-perf version from 2023-05-30
- const PERF_COMMIT: &str = "8b2ac3042e1ff2c0074455a0a3618adef97156b1";
+ // rustc-perf version from 2023-10-22
+ const PERF_COMMIT: &str = "4f313add609f43e928e98132358e8426ed3969ae";
let url = format!("https://ci-mirrors.rust-lang.org/rustc/rustc-perf-{PERF_COMMIT}.zip");
let client = reqwest::blocking::Client::builder()
diff --git a/src/tools/opt-dist/src/tests.rs b/src/tools/opt-dist/src/tests.rs
index 31aabca09..8a8f98a5e 100644
--- a/src/tools/opt-dist/src/tests.rs
+++ b/src/tools/opt-dist/src/tests.rs
@@ -61,7 +61,7 @@ pub fn run_tests(env: &Environment) -> anyhow::Result<()> {
let config_content = format!(
r#"profile = "user"
-changelog-seen = 2
+change-id = 115898
[build]
rustc = "{rustc}"
@@ -84,6 +84,8 @@ llvm-config = "{llvm_config}"
env.python_binary(),
x_py.as_str(),
"test",
+ "--build",
+ env.host_triple(),
"--stage",
"0",
"tests/assembly",
diff --git a/src/tools/opt-dist/src/training.rs b/src/tools/opt-dist/src/training.rs
index 274f4cea0..46040e32a 100644
--- a/src/tools/opt-dist/src/training.rs
+++ b/src/tools/opt-dist/src/training.rs
@@ -27,8 +27,6 @@ const RUSTC_PGO_CRATES: &[&str] = &[
"bitmaps-3.1.0",
];
-const LLVM_BOLT_CRATES: &[&str] = LLVM_PGO_CRATES;
-
fn init_compiler_benchmarks(
env: &Environment,
profiles: &[&str],
@@ -113,6 +111,14 @@ fn log_profile_stats(
Ok(())
}
+pub fn llvm_benchmarks(env: &Environment) -> CmdBuilder {
+ init_compiler_benchmarks(env, &["Debug", "Opt"], &["Full"], LLVM_PGO_CRATES)
+}
+
+pub fn rustc_benchmarks(env: &Environment) -> CmdBuilder {
+ init_compiler_benchmarks(env, &["Check", "Debug", "Opt"], &["All"], RUSTC_PGO_CRATES)
+}
+
pub struct LlvmPGOProfile(pub Utf8PathBuf);
pub fn gather_llvm_profiles(
@@ -122,9 +128,7 @@ pub fn gather_llvm_profiles(
log::info!("Running benchmarks with PGO instrumented LLVM");
with_log_group("Running benchmarks", || {
- init_compiler_benchmarks(env, &["Debug", "Opt"], &["Full"], LLVM_PGO_CRATES)
- .run()
- .context("Cannot gather LLVM PGO profiles")
+ llvm_benchmarks(env).run().context("Cannot gather LLVM PGO profiles")
})?;
let merged_profile = env.artifact_dir().join("llvm-pgo.profdata");
@@ -157,7 +161,7 @@ pub fn gather_rustc_profiles(
// 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.
with_log_group("Running benchmarks", || {
- init_compiler_benchmarks(env, &["Check", "Debug", "Opt"], &["All"], RUSTC_PGO_CRATES)
+ rustc_benchmarks(env)
.env("LLVM_PROFILE_FILE", profile_template.as_str())
.run()
.context("Cannot gather rustc PGO profiles")
@@ -176,23 +180,25 @@ pub fn gather_rustc_profiles(
Ok(RustcPGOProfile(merged_profile))
}
-pub struct LlvmBoltProfile(pub Utf8PathBuf);
+pub struct BoltProfile(pub Utf8PathBuf);
-pub fn gather_llvm_bolt_profiles(env: &Environment) -> anyhow::Result<LlvmBoltProfile> {
- log::info!("Running benchmarks with BOLT instrumented LLVM");
+pub fn gather_bolt_profiles(
+ env: &Environment,
+ name: &str,
+ benchmarks: CmdBuilder,
+ profile_prefix: &Utf8Path,
+) -> anyhow::Result<BoltProfile> {
+ log::info!("Running benchmarks with BOLT instrumented {name}");
with_log_group("Running benchmarks", || {
- init_compiler_benchmarks(env, &["Check", "Debug", "Opt"], &["Full"], LLVM_BOLT_CRATES)
- .run()
- .context("Cannot gather LLVM BOLT profiles")
+ benchmarks.run().with_context(|| "Cannot gather {name} BOLT profiles")
})?;
- let merged_profile = env.artifact_dir().join("llvm-bolt.profdata");
- let profile_root = Utf8PathBuf::from("/tmp/prof.fdata");
- log::info!("Merging LLVM BOLT profiles to {merged_profile}");
+ let merged_profile = env.artifact_dir().join(format!("{name}-bolt.profdata"));
+ log::info!("Merging {name} BOLT profiles from {profile_prefix} to {merged_profile}");
let profiles: Vec<_> =
- glob::glob(&format!("{profile_root}*"))?.collect::<Result<Vec<_>, _>>()?;
+ glob::glob(&format!("{profile_prefix}*"))?.collect::<Result<Vec<_>, _>>()?;
let mut merge_args = vec!["merge-fdata"];
merge_args.extend(profiles.iter().map(|p| p.to_str().unwrap()));
@@ -204,7 +210,7 @@ pub fn gather_llvm_bolt_profiles(env: &Environment) -> anyhow::Result<LlvmBoltPr
.context("Cannot merge BOLT profiles")
})?;
- log::info!("LLVM BOLT statistics");
+ log::info!("{name} BOLT statistics");
log::info!(
"{merged_profile}: {}",
humansize::format_size(std::fs::metadata(merged_profile.as_std_path())?.len(), BINARY)
@@ -216,8 +222,17 @@ pub fn gather_llvm_bolt_profiles(env: &Environment) -> anyhow::Result<LlvmBoltPr
.collect::<Result<Vec<_>, _>>()?
.into_iter()
.sum::<u64>();
- log::info!("{profile_root}: {}", humansize::format_size(size, BINARY));
+ log::info!("{profile_prefix}: {}", humansize::format_size(size, BINARY));
log::info!("Profile file count: {}", profiles.len());
- Ok(LlvmBoltProfile(merged_profile))
+ // Delete the gathered profiles
+ for profile in glob::glob(&format!("{profile_prefix}*"))?.into_iter() {
+ if let Ok(profile) = profile {
+ if let Err(error) = std::fs::remove_file(&profile) {
+ log::error!("Cannot delete BOLT profile {}: {error:?}", profile.display());
+ }
+ }
+ }
+
+ Ok(BoltProfile(merged_profile))
}
diff --git a/src/tools/opt-dist/src/utils/artifact_size.rs b/src/tools/opt-dist/src/utils/artifact_size.rs
new file mode 100644
index 000000000..4dc8952b6
--- /dev/null
+++ b/src/tools/opt-dist/src/utils/artifact_size.rs
@@ -0,0 +1,61 @@
+use std::io::Write;
+
+use tabled::builder::Builder;
+use tabled::settings::object::Columns;
+use tabled::settings::style::{BorderChar, Offset};
+use tabled::settings::{Modify, Style};
+
+use crate::environment::Environment;
+use crate::utils::io::get_files_from_dir;
+
+pub fn print_binary_sizes(env: &Environment) -> anyhow::Result<()> {
+ use humansize::format_size;
+ use humansize::BINARY;
+ use std::fmt::Write;
+
+ let root = env.build_artifacts().join("stage2");
+
+ let mut files = get_files_from_dir(&root.join("bin"), None)?;
+ files.extend(get_files_from_dir(&root.join("lib"), Some(".so"))?);
+ files.sort_unstable();
+
+ let items: Vec<_> = files
+ .into_iter()
+ .map(|file| {
+ let size = std::fs::metadata(file.as_std_path()).map(|m| m.len()).unwrap_or(0);
+ let size_formatted = format_size(size, BINARY);
+ let name = file.file_name().unwrap().to_string();
+ (name, size_formatted)
+ })
+ .collect();
+
+ // Write to log
+ let mut output = String::new();
+ for (name, size_formatted) in items.iter() {
+ let name = format!("{}:", name);
+ writeln!(output, "{name:<50}{size_formatted:>10}")?;
+ }
+ log::info!("Rustc artifact size\n{output}");
+
+ // Write to GitHub summary
+ if let Ok(summary_path) = std::env::var("GITHUB_STEP_SUMMARY") {
+ let mut builder = Builder::default();
+ for (name, size_formatted) in items {
+ builder.push_record(vec![name, size_formatted]);
+ }
+
+ builder.set_header(vec!["Artifact", "Size"]);
+ let mut table = builder.build();
+
+ let mut file = std::fs::File::options().append(true).create(true).open(summary_path)?;
+ writeln!(
+ file,
+ "# Artifact size\n{}\n",
+ table.with(Style::markdown()).with(
+ Modify::new(Columns::single(1)).with(BorderChar::horizontal(':', Offset::End(0))),
+ )
+ )?;
+ }
+
+ Ok(())
+}
diff --git a/src/tools/opt-dist/src/utils/mod.rs b/src/tools/opt-dist/src/utils/mod.rs
index 6fc96592a..ca1292dd5 100644
--- a/src/tools/opt-dist/src/utils/mod.rs
+++ b/src/tools/opt-dist/src/utils/mod.rs
@@ -1,10 +1,13 @@
-pub mod io;
+use sysinfo::{DiskExt, RefreshKind, System, SystemExt};
use crate::environment::Environment;
-use crate::utils::io::{delete_directory, get_files_from_dir};
-use humansize::{format_size, BINARY};
+use crate::timer::Timer;
+use crate::utils::io::delete_directory;
+use humansize::BINARY;
use std::time::Duration;
-use sysinfo::{DiskExt, RefreshKind, System, SystemExt};
+
+pub mod artifact_size;
+pub mod io;
pub fn format_env_variables() -> String {
let vars = std::env::vars().map(|(key, value)| format!("{key}={value}")).collect::<Vec<_>>();
@@ -26,28 +29,6 @@ pub fn print_free_disk_space() -> anyhow::Result<()> {
Ok(())
}
-pub fn print_binary_sizes(env: &Environment) -> anyhow::Result<()> {
- use std::fmt::Write;
-
- let root = env.build_artifacts().join("stage2");
-
- let mut files = get_files_from_dir(&root.join("bin"), None)?;
- files.extend(get_files_from_dir(&root.join("lib"), Some(".so"))?);
- files.sort_unstable();
-
- let mut output = String::new();
- for file in files {
- let size = std::fs::metadata(file.as_std_path())?.len();
- let size_formatted = format_size(size, BINARY);
- let name = format!("{}:", file.file_name().unwrap());
- writeln!(output, "{name:<50}{size_formatted:>10}")?;
- }
-
- log::info!("Rustc artifact size\n{output}");
-
- Ok(())
-}
-
pub fn clear_llvm_files(env: &Environment) -> anyhow::Result<()> {
// Bootstrap currently doesn't support rebuilding LLVM when PGO options
// change (or any other llvm-related options); so just clear out the relevant
@@ -58,6 +39,24 @@ pub fn clear_llvm_files(env: &Environment) -> anyhow::Result<()> {
Ok(())
}
+/// Write the formatted statistics of the timer to a Github Actions summary.
+pub fn write_timer_to_summary(path: &str, timer: &Timer) -> anyhow::Result<()> {
+ use std::io::Write;
+
+ let mut file = std::fs::File::options().append(true).create(true).open(path)?;
+ writeln!(
+ file,
+ r#"# Step durations
+
+```
+{}
+```
+"#,
+ timer.format_stats()
+ )?;
+ Ok(())
+}
+
/// Wraps all output produced within the `func` closure in a CI output group, if we're running in
/// CI.
pub fn with_log_group<F: FnOnce() -> R, R>(group: &str, func: F) -> R {
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/attr/builtin.rs b/src/tools/rust-analyzer/crates/hir-def/src/attr/builtin.rs
index 152f05b2c..2ae3cd2a9 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/attr/builtin.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/attr/builtin.rs
@@ -262,24 +262,6 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
naked_functions, experimental!(naked)
),
- // Plugins:
- // BuiltinAttribute {
- // name: sym::plugin,
- // only_local: false,
- // type_: CrateLevel,
- // template: template!(List: "name"),
- // duplicates: DuplicatesOk,
- // gate: Gated(
- // Stability::Deprecated(
- // "https://github.com/rust-lang/rust/pull/64675",
- // Some("may be removed in a future compiler version"),
- // ),
- // sym::plugin,
- // "compiler plugins are deprecated",
- // cfg_fn!(plugin)
- // ),
- // },
-
// Testing:
gated!(
test_runner, CrateLevel, template!(List: "path"), ErrorFollowing, custom_test_frameworks,
diff --git a/src/tools/rust-analyzer/crates/parser/src/syntax_kind.rs b/src/tools/rust-analyzer/crates/parser/src/syntax_kind.rs
index 0483adc77..3ca6bd4cb 100644
--- a/src/tools/rust-analyzer/crates/parser/src/syntax_kind.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/syntax_kind.rs
@@ -4,7 +4,7 @@
mod generated;
#[allow(unreachable_pub)]
-pub use self::generated::{SyntaxKind, T};
+pub use self::generated::SyntaxKind;
impl From<u16> for SyntaxKind {
#[inline]
diff --git a/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs b/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs
index db5278f89..4b5890376 100644
--- a/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs
@@ -497,4 +497,3 @@ impl SyntaxKind {
}
#[macro_export]
macro_rules ! T { [;] => { $ crate :: SyntaxKind :: SEMICOLON } ; [,] => { $ crate :: SyntaxKind :: COMMA } ; ['('] => { $ crate :: SyntaxKind :: L_PAREN } ; [')'] => { $ crate :: SyntaxKind :: R_PAREN } ; ['{'] => { $ crate :: SyntaxKind :: L_CURLY } ; ['}'] => { $ crate :: SyntaxKind :: R_CURLY } ; ['['] => { $ crate :: SyntaxKind :: L_BRACK } ; [']'] => { $ crate :: SyntaxKind :: R_BRACK } ; [<] => { $ crate :: SyntaxKind :: L_ANGLE } ; [>] => { $ crate :: SyntaxKind :: R_ANGLE } ; [@] => { $ crate :: SyntaxKind :: AT } ; [#] => { $ crate :: SyntaxKind :: POUND } ; [~] => { $ crate :: SyntaxKind :: TILDE } ; [?] => { $ crate :: SyntaxKind :: QUESTION } ; [$] => { $ crate :: SyntaxKind :: DOLLAR } ; [&] => { $ crate :: SyntaxKind :: AMP } ; [|] => { $ crate :: SyntaxKind :: PIPE } ; [+] => { $ crate :: SyntaxKind :: PLUS } ; [*] => { $ crate :: SyntaxKind :: STAR } ; [/] => { $ crate :: SyntaxKind :: SLASH } ; [^] => { $ crate :: SyntaxKind :: CARET } ; [%] => { $ crate :: SyntaxKind :: PERCENT } ; [_] => { $ crate :: SyntaxKind :: UNDERSCORE } ; [.] => { $ crate :: SyntaxKind :: DOT } ; [..] => { $ crate :: SyntaxKind :: DOT2 } ; [...] => { $ crate :: SyntaxKind :: DOT3 } ; [..=] => { $ crate :: SyntaxKind :: DOT2EQ } ; [:] => { $ crate :: SyntaxKind :: COLON } ; [::] => { $ crate :: SyntaxKind :: COLON2 } ; [=] => { $ crate :: SyntaxKind :: EQ } ; [==] => { $ crate :: SyntaxKind :: EQ2 } ; [=>] => { $ crate :: SyntaxKind :: FAT_ARROW } ; [!] => { $ crate :: SyntaxKind :: BANG } ; [!=] => { $ crate :: SyntaxKind :: NEQ } ; [-] => { $ crate :: SyntaxKind :: MINUS } ; [->] => { $ crate :: SyntaxKind :: THIN_ARROW } ; [<=] => { $ crate :: SyntaxKind :: LTEQ } ; [>=] => { $ crate :: SyntaxKind :: GTEQ } ; [+=] => { $ crate :: SyntaxKind :: PLUSEQ } ; [-=] => { $ crate :: SyntaxKind :: MINUSEQ } ; [|=] => { $ crate :: SyntaxKind :: PIPEEQ } ; [&=] => { $ crate :: SyntaxKind :: AMPEQ } ; [^=] => { $ crate :: SyntaxKind :: CARETEQ } ; [/=] => { $ crate :: SyntaxKind :: SLASHEQ } ; [*=] => { $ crate :: SyntaxKind :: STAREQ } ; [%=] => { $ crate :: SyntaxKind :: PERCENTEQ } ; [&&] => { $ crate :: SyntaxKind :: AMP2 } ; [||] => { $ crate :: SyntaxKind :: PIPE2 } ; [<<] => { $ crate :: SyntaxKind :: SHL } ; [>>] => { $ crate :: SyntaxKind :: SHR } ; [<<=] => { $ crate :: SyntaxKind :: SHLEQ } ; [>>=] => { $ crate :: SyntaxKind :: SHREQ } ; [as] => { $ crate :: SyntaxKind :: AS_KW } ; [async] => { $ crate :: SyntaxKind :: ASYNC_KW } ; [await] => { $ crate :: SyntaxKind :: AWAIT_KW } ; [box] => { $ crate :: SyntaxKind :: BOX_KW } ; [break] => { $ crate :: SyntaxKind :: BREAK_KW } ; [const] => { $ crate :: SyntaxKind :: CONST_KW } ; [continue] => { $ crate :: SyntaxKind :: CONTINUE_KW } ; [crate] => { $ crate :: SyntaxKind :: CRATE_KW } ; [do] => { $ crate :: SyntaxKind :: DO_KW } ; [dyn] => { $ crate :: SyntaxKind :: DYN_KW } ; [else] => { $ crate :: SyntaxKind :: ELSE_KW } ; [enum] => { $ crate :: SyntaxKind :: ENUM_KW } ; [extern] => { $ crate :: SyntaxKind :: EXTERN_KW } ; [false] => { $ crate :: SyntaxKind :: FALSE_KW } ; [fn] => { $ crate :: SyntaxKind :: FN_KW } ; [for] => { $ crate :: SyntaxKind :: FOR_KW } ; [if] => { $ crate :: SyntaxKind :: IF_KW } ; [impl] => { $ crate :: SyntaxKind :: IMPL_KW } ; [in] => { $ crate :: SyntaxKind :: IN_KW } ; [let] => { $ crate :: SyntaxKind :: LET_KW } ; [loop] => { $ crate :: SyntaxKind :: LOOP_KW } ; [macro] => { $ crate :: SyntaxKind :: MACRO_KW } ; [match] => { $ crate :: SyntaxKind :: MATCH_KW } ; [mod] => { $ crate :: SyntaxKind :: MOD_KW } ; [move] => { $ crate :: SyntaxKind :: MOVE_KW } ; [mut] => { $ crate :: SyntaxKind :: MUT_KW } ; [pub] => { $ crate :: SyntaxKind :: PUB_KW } ; [ref] => { $ crate :: SyntaxKind :: REF_KW } ; [return] => { $ crate :: SyntaxKind :: RETURN_KW } ; [self] => { $ crate :: SyntaxKind :: SELF_KW } ; [Self] => { $ crate :: SyntaxKind :: SELF_TYPE_KW } ; [static] => { $ crate :: SyntaxKind :: STATIC_KW } ; [struct] => { $ crate :: SyntaxKind :: STRUCT_KW } ; [super] => { $ crate :: SyntaxKind :: SUPER_KW } ; [trait] => { $ crate :: SyntaxKind :: TRAIT_KW } ; [true] => { $ crate :: SyntaxKind :: TRUE_KW } ; [try] => { $ crate :: SyntaxKind :: TRY_KW } ; [type] => { $ crate :: SyntaxKind :: TYPE_KW } ; [unsafe] => { $ crate :: SyntaxKind :: UNSAFE_KW } ; [use] => { $ crate :: SyntaxKind :: USE_KW } ; [where] => { $ crate :: SyntaxKind :: WHERE_KW } ; [while] => { $ crate :: SyntaxKind :: WHILE_KW } ; [yield] => { $ crate :: SyntaxKind :: YIELD_KW } ; [auto] => { $ crate :: SyntaxKind :: AUTO_KW } ; [builtin] => { $ crate :: SyntaxKind :: BUILTIN_KW } ; [default] => { $ crate :: SyntaxKind :: DEFAULT_KW } ; [existential] => { $ crate :: SyntaxKind :: EXISTENTIAL_KW } ; [union] => { $ crate :: SyntaxKind :: UNION_KW } ; [raw] => { $ crate :: SyntaxKind :: RAW_KW } ; [macro_rules] => { $ crate :: SyntaxKind :: MACRO_RULES_KW } ; [yeet] => { $ crate :: SyntaxKind :: YEET_KW } ; [offset_of] => { $ crate :: SyntaxKind :: OFFSET_OF_KW } ; [asm] => { $ crate :: SyntaxKind :: ASM_KW } ; [format_args] => { $ crate :: SyntaxKind :: FORMAT_ARGS_KW } ; [lifetime_ident] => { $ crate :: SyntaxKind :: LIFETIME_IDENT } ; [ident] => { $ crate :: SyntaxKind :: IDENT } ; [shebang] => { $ crate :: SyntaxKind :: SHEBANG } ; }
-pub use T;
diff --git a/src/tools/rust-analyzer/crates/syntax/src/tests/sourcegen_ast.rs b/src/tools/rust-analyzer/crates/syntax/src/tests/sourcegen_ast.rs
index 56227fce9..dc6c96343 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/tests/sourcegen_ast.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/tests/sourcegen_ast.rs
@@ -450,7 +450,6 @@ fn generate_syntax_kinds(grammar: KindsSrc<'_>) -> String {
[ident] => { $crate::SyntaxKind::IDENT };
[shebang] => { $crate::SyntaxKind::SHEBANG };
}
- pub use T;
};
sourcegen::add_preamble("sourcegen_ast", sourcegen::reformat(ast.to_string()))
diff --git a/src/tools/rust-installer/Cargo.toml b/src/tools/rust-installer/Cargo.toml
index 471f2b5ac..cdd867c5f 100644
--- a/src/tools/rust-installer/Cargo.toml
+++ b/src/tools/rust-installer/Cargo.toml
@@ -16,7 +16,6 @@ rayon = "1.0"
tar = "0.4.38"
walkdir = "2"
xz2 = "0.1.4"
-num_cpus = "1"
[dependencies.clap]
features = ["derive"]
diff --git a/src/tools/rustdoc-gui/tester.js b/src/tools/rustdoc-gui/tester.js
index af1bc05dd..8f6626f62 100644
--- a/src/tools/rustdoc-gui/tester.js
+++ b/src/tools/rustdoc-gui/tester.js
@@ -249,12 +249,17 @@ async function main(argv) {
console.log("`--no-headless` option is active, disabling concurrency for running tests.");
}
- console.log(`Running ${files.length} rustdoc-gui (${opts["jobs"]} concurrently) ...`);
-
if (opts["jobs"] < 1) {
+ const len = files.length;
+ console.log(
+ `Running ${len} rustdoc-gui (UNBOUNDED concurrency; use "-j#" for a limit) ...`,
+ );
process.setMaxListeners(files.length + 1);
} else if (headless) {
+ console.log(`Running ${files.length} rustdoc-gui (${opts["jobs"]} concurrently) ...`);
process.setMaxListeners(opts["jobs"] + 1);
+ } else {
+ console.log(`Running ${files.length} rustdoc-gui ...`);
}
// We catch this "event" to display a nicer message in case of unexpected exit (because of a
diff --git a/src/tools/rustfmt/.github/workflows/check_diff.yml b/src/tools/rustfmt/.github/workflows/check_diff.yml
index 8bfb58345..2f2beb769 100644
--- a/src/tools/rustfmt/.github/workflows/check_diff.yml
+++ b/src/tools/rustfmt/.github/workflows/check_diff.yml
@@ -30,4 +30,4 @@ jobs:
rustup target add x86_64-unknown-linux-gnu
- name: check diff
- run: bash ${GITHUB_WORKSPACE}/ci/check_diff.sh ${{ github.event.inputs.clone_url }} ${{ github.event.inputs.branch_name }} ${{ github.event.inputs.commit_hash }} ${{ github.event.inputs.rustfmt_configs }}
+ run: bash ${GITHUB_WORKSPACE}/ci/check_diff.sh ${{ github.event.inputs.clone_url }} ${{ github.event.inputs.branch_name }} ${{ github.event.inputs.commit_hash || github.event.inputs.branch_name }} ${{ github.event.inputs.rustfmt_configs }}
diff --git a/src/tools/rustfmt/CHANGELOG.md b/src/tools/rustfmt/CHANGELOG.md
index fbcd0a57f..ec4c682d2 100644
--- a/src/tools/rustfmt/CHANGELOG.md
+++ b/src/tools/rustfmt/CHANGELOG.md
@@ -3,6 +3,82 @@
## [Unreleased]
+## [1.7.0] 2023-10-22
+
+### Fixed
+
+- Sometimes when `format_code_in_doc_comments=true` was set some line comments were converted to block comments [#5533](https://github.com/rust-lang/rustfmt/issues/5533)
+- rustfmt will no longer remove the braces in match arms when the block has a labeled [#5676](https://github.com/rust-lang/rustfmt/issues/5676)
+ ```rust
+ fn main() {
+ match true {
+ true => 'a: {
+ break 'a
+ }
+ _ => (),
+ }
+ }
+ ```
+- Calling methods on float literals ending in `.` will now be wrapped in parenthesis. e.g. `0. .to_string()` will be formatted as `(0.).to_string()` [#5791](https://github.com/rust-lang/rustfmt/issues/5791)
+- Prevent ICE when formatting empty `macro_rules!` branch [#5730](https://github.com/rust-lang/rustfmt/issues/5730)
+ ```rust
+ macro_rules! statement {
+ () => {;};
+ }
+ ```
+- Prevent ICE when formatting `vec!{}` [#5735](https://github.com/rust-lang/rustfmt/issues/5735)
+- Prevent internal trailing whitespace error when formatting an empty `macro_rules!` defintion e.g. `macro_rules! foo {}` [#5882](https://github.com/rust-lang/rustfmt/issues/5882)
+- Formatting doc comment lines that start with `.` or `)` won't be treated as ordered markdown lists because `.` or `)` must be preceded by a number to start an ordered markdown list [#5835](https://github.com/rust-lang/rustfmt/pull/5835)
+- Add parenthesis around closures when they're used as method receives, don't have a block body, and end with `.` [#4808](https://github.com/rust-lang/rustfmt/issues/4808)
+ ```rust
+ fn main() {
+ || (10.).method();
+ (|| ..).method();
+ (|| 1..).method();
+ }
+ ```
+- Prevent removing `for<T>` when using the [`#![feature(non_lifetime_binders)]`](https://github.com/rust-lang/rust/issues/108185) [#5721](https://github.com/rust-lang/rustfmt/issues/5721)
+ ```rust
+ #![feature(non_lifetime_binders)]
+ #![allow(incomplete_features)]
+
+ trait Other<U: ?Sized> {}
+
+ trait Trait<U>
+ where
+ for<T> U: Other<T> {}
+ ```
+- Fix various issues with comments in imports [#5852](https://github.com/rust-lang/rustfmt/issues/5852) [#4708](https://github.com/rust-lang/rustfmt/issues/4708) [#3984](https://github.com/rust-lang/rustfmt/issues/3984)
+- When setting `version = Two` newlines between where clause bounds will be removed [#5655](https://github.com/rust-lang/rustfmt/issues/5655)
+ ```rust
+ fn foo<T>(_: T)
+ where
+ T: std::fmt::Debug,
+ T: std::fmt::Display,
+ {
+ }
+ ```
+- Improve formatting of `let-else` statements that have leading attributes When setting `version = Two` [#5901](https://github.com/rust-lang/rustfmt/issues/5901)
+- Prevent comment duplication in expressions wrapped in parenthesis. [#5871](https://github.com/rust-lang/rustfmt/issues/5871)
+- Adjust the span derivation used when rewriting const generics. The incorrect span derivation lead to invalid code after reformatting. [#5935](https://github.com/rust-lang/rustfmt/issues/5935)
+
+
+### Changed
+
+- rustfmt no longer removes explicit `Rust` ABIs. e.g `extern "Rust" fn im_a_rust_fn() {}` [#5701](https://github.com/rust-lang/rustfmt/issues/5701)
+- Setting `trailing_semicolon = false` will only remove trailing `;` on the last expression in a block [#5797](https://github.com/rust-lang/rustfmt/issues/5797)
+- Update the format of `cargo help fmt` to be more consistent with other standard commands [#5908](https://github.com/rust-lang/rustfmt/pull/5908)
+
+### Added
+
+- Users can now set `skip_macro_invocations` in `rustfmt.toml` [#5816](https://github.com/rust-lang/rustfmt/issues/5816)
+- Adds initial support for formatting `let-chains`. **`let-chains` are still a nightly feature and their formatting is subject to change** [#5910](https://github.com/rust-lang/rustfmt/pull/5910). Formatting was implemented following the rules outlined in [rust-lang/rust#110568](https://github.com/rust-lang/rust/pull/110568)
+
+### Misc
+
+- Support the experimental `dyn*` syntax, enabled by `#![feature(dyn_star)]` [#5542](https://github.com/rust-lang/rustfmt/issues/5542)
+- Replace `unicode_categories` dependency with `unicode-properties` [#5864](https://github.com/rust-lang/rustfmt/pull/5864)
+
## [1.6.0] 2023-07-02
### Added
@@ -10,7 +86,7 @@
- Support for formatting let-else statements [#5690]
- New config option, `single_line_let_else_max_width`, that allows users to configure the maximum length of single line `let-else` statements. `let-else` statements that otherwise meet the requirements to be formatted on a single line will have their divergent`else` block formatted over multiple lines if they exceed this length [#5684]
-[#5690]: (https://github.com/rust-lang/rustfmt/pulls/5690)
+[#5690]: https://github.com/rust-lang/rustfmt/pull/5690
[#5684]: https://github.com/rust-lang/rustfmt/issues/5684
## [1.5.3] 2023-06-20
@@ -19,7 +95,7 @@
- When formatting doc comments with `wrap_comments = true` rustfmt will no longer wrap markdown tables [#4210](https://github.com/rust-lang/rustfmt/issues/4210)
- Properly handle wrapping comments that include a numbered list in markdown [#5416](https://github.com/rust-lang/rustfmt/issues/5416)
-- Properly handle markdown sublists that utilize a `+` [#4041](https://github.com/rust-lang/rustfmt/issues/4210)
+- Properly handle markdown sublists that utilize a `+` [#4041](https://github.com/rust-lang/rustfmt/issues/4041)
- rustfmt will no longer use shorthand initialization when rewriting a tuple struct even when `use_field_init_shorthand = true` as this leads to code that could no longer compile.
Take the following struct as an example `struct MyStruct(u64);`. rustfmt will no longer format `MyStruct { 0: 0 }` as `MyStruct { 0 }` [#5488](https://github.com/rust-lang/rustfmt/issues/5488)
- rustfmt no longer panics when formatting an empty code block in a doc comment with `format_code_in_doc_comments = true` [#5234](https://github.com/rust-lang/rustfmt/issues/5234). For example:
diff --git a/src/tools/rustfmt/CODE_OF_CONDUCT.md b/src/tools/rustfmt/CODE_OF_CONDUCT.md
index d70b2b52a..2acddfeef 100644
--- a/src/tools/rustfmt/CODE_OF_CONDUCT.md
+++ b/src/tools/rustfmt/CODE_OF_CONDUCT.md
@@ -11,7 +11,7 @@ A version of this document [can be found online](https://www.rust-lang.org/condu
* Please be kind and courteous. There's no need to be mean or rude.
* Respect that people have differences of opinion and that every design or implementation choice carries a trade-off and numerous costs. There is seldom a right answer.
* Please keep unstructured critique to a minimum. If you have solid ideas you want to experiment with, make a fork and see how it works.
-* We will exclude you from interaction if you insult, demean or harass anyone. That is not welcome behavior. We interpret the term "harassment" as including the definition in the <a href="http://citizencodeofconduct.org/">Citizen Code of Conduct</a>; if you have any lack of clarity about what might be included in that concept, please read their definition. In particular, we don't tolerate behavior that excludes people in socially marginalized groups.
+* We will exclude you from interaction if you insult, demean or harass anyone. That is not welcome behavior. We interpret the term "harassment" as including the definition in the <a href="https://github.com/stumpsyn/policies/blob/master/citizen_code_of_conduct.md/">Citizen Code of Conduct</a>; if you have any lack of clarity about what might be included in that concept, please read their definition. In particular, we don't tolerate behavior that excludes people in socially marginalized groups.
* Private harassment is also unacceptable. No matter who you are, if you feel you have been or are being harassed or made uncomfortable by a community member, please contact one of the channel ops or any of the [Rust moderation team][mod_team] immediately. Whether you're a regular contributor or a newcomer, we care about making this community a safe place for you and we've got your back.
* Likewise any spamming, trolling, flaming, baiting or other attention-stealing behavior is not welcome.
diff --git a/src/tools/rustfmt/Cargo.lock b/src/tools/rustfmt/Cargo.lock
index bd28df7a7..8fcefa974 100644
--- a/src/tools/rustfmt/Cargo.lock
+++ b/src/tools/rustfmt/Cargo.lock
@@ -23,42 +23,50 @@ dependencies = [
[[package]]
name = "anstream"
-version = "0.2.6"
+version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "342258dd14006105c2b75ab1bd7543a03bdf0cfc94383303ac212a04939dff6f"
+checksum = "b1f58811cfac344940f1a400b6e6231ce35171f614f26439e80f8c1465c5cc0c"
dependencies = [
"anstyle",
"anstyle-parse",
+ "anstyle-query",
"anstyle-wincon",
- "concolor-override",
- "concolor-query",
- "is-terminal",
+ "colorchoice",
"utf8parse",
]
[[package]]
name = "anstyle"
-version = "0.3.5"
+version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "23ea9e81bd02e310c216d080f6223c179012256e5151c41db88d12c88a1684d2"
+checksum = "b84bf0a05bbb2a83e5eb6fa36bb6e87baa08193c35ff52bbf6b38d8af2890e46"
[[package]]
name = "anstyle-parse"
-version = "0.1.1"
+version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a7d1bb534e9efed14f3e5f44e7dd1a4f709384023a4165199a4241e18dff0116"
+checksum = "938874ff5980b03a87c5524b3ae5b59cf99b1d6bc836848df7bc5ada9643c333"
dependencies = [
"utf8parse",
]
[[package]]
+name = "anstyle-query"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b"
+dependencies = [
+ "windows-sys",
+]
+
+[[package]]
name = "anstyle-wincon"
-version = "0.2.0"
+version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c3127af6145b149f3287bb9a0d10ad9c5692dba8c53ad48285e5bec4063834fa"
+checksum = "58f54d10c6dfa51283a066ceab3ec1ab78d13fae00aa49243a45e4571fb79dfd"
dependencies = [
"anstyle",
- "windows-sys 0.45.0",
+ "windows-sys",
]
[[package]]
@@ -90,11 +98,11 @@ dependencies = [
[[package]]
name = "bytecount"
-version = "0.6.2"
+version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "72feb31ffc86498dacdbd0fcebb56138e7177a8cc5cea4516031d15ae85a742e"
+checksum = "ad152d03a2c813c80bb94fedbf3a3f02b28f793e39e7c214c8a0bcc196343de7"
dependencies = [
- "packed_simd_2",
+ "packed_simd",
]
[[package]]
@@ -130,12 +138,6 @@ dependencies = [
]
[[package]]
-name = "cc"
-version = "1.0.79"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
-
-[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -143,33 +145,41 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "clap"
-version = "4.2.1"
+version = "4.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "046ae530c528f252094e4a77886ee1374437744b2bff1497aa898bbddbbb29b3"
+checksum = "6a13b88d2c62ff462f88e4a121f17a82c1af05693a2f192b5c38d14de73c19f6"
dependencies = [
"clap_builder",
"clap_derive",
- "once_cell",
+]
+
+[[package]]
+name = "clap-cargo"
+version = "0.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "383f21342a464d4af96e9a4cad22a0b4f2880d4a5b3bbf5c9654dd1d9a224ee4"
+dependencies = [
+ "anstyle",
+ "clap",
]
[[package]]
name = "clap_builder"
-version = "4.2.1"
+version = "4.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "223163f58c9a40c3b0a43e1c4b50a9ce09f007ea2cb1ec258a687945b4b7929f"
+checksum = "2bb9faaa7c2ef94b2743a21f5a29e6f0010dff4caa69ac8e9d6cf8b6fa74da08"
dependencies = [
"anstream",
"anstyle",
- "bitflags",
"clap_lex",
"strsim",
]
[[package]]
name = "clap_derive"
-version = "4.2.0"
+version = "4.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3f9644cd56d6b87dbe899ef8b053e331c0637664e9e21a33dfcdc36093f5c5c4"
+checksum = "0862016ff20d69b84ef8247369fabf5c008a7417002411897d40ee1f4532b873"
dependencies = [
"heck",
"proc-macro2",
@@ -179,24 +189,15 @@ dependencies = [
[[package]]
name = "clap_lex"
-version = "0.4.1"
+version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8a2dd5a6fe8c6e3502f568a6353e5273bbb15193ad9a89e457b9970798efbea1"
+checksum = "cd7cc57abe963c6d3b9d8be5b06ba7c8957a930305ca90304f24ef040aa6f961"
[[package]]
-name = "concolor-override"
+name = "colorchoice"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a855d4a1978dc52fb0536a04d384c2c0c1aa273597f08b77c8c4d3b2eec6037f"
-
-[[package]]
-name = "concolor-query"
-version = "0.3.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "88d11d52c3d7ca2e6d0040212be9e4dbbcd78b6447f535b6b561f449427944cf"
-dependencies = [
- "windows-sys 0.45.0",
-]
+checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
[[package]]
name = "crossbeam-utils"
@@ -262,40 +263,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
[[package]]
-name = "env_logger"
-version = "0.10.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0"
-dependencies = [
- "humantime",
- "is-terminal",
- "log",
- "regex",
- "termcolor",
-]
-
-[[package]]
-name = "errno"
-version = "0.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a"
-dependencies = [
- "errno-dragonfly",
- "libc",
- "windows-sys 0.48.0",
-]
-
-[[package]]
-name = "errno-dragonfly"
-version = "0.1.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
-dependencies = [
- "cc",
- "libc",
-]
-
-[[package]]
name = "fnv"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -347,18 +314,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9"
[[package]]
-name = "hermit-abi"
-version = "0.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286"
-
-[[package]]
-name = "humantime"
-version = "2.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
-
-[[package]]
name = "ignore"
version = "0.4.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -387,29 +342,6 @@ dependencies = [
]
[[package]]
-name = "io-lifetimes"
-version = "1.0.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220"
-dependencies = [
- "hermit-abi",
- "libc",
- "windows-sys 0.48.0",
-]
-
-[[package]]
-name = "is-terminal"
-version = "0.4.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f"
-dependencies = [
- "hermit-abi",
- "io-lifetimes",
- "rustix",
- "windows-sys 0.48.0",
-]
-
-[[package]]
name = "itertools"
version = "0.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -438,15 +370,9 @@ checksum = "3304a64d199bb964be99741b7a14d26972741915b3649639149b2479bb46f4b5"
[[package]]
name = "libm"
-version = "0.1.4"
+version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7fc7aa29613bd6a620df431842069224d8bc9011086b1db4c0e0cd47fa03ec9a"
-
-[[package]]
-name = "linux-raw-sys"
-version = "0.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d59d8c75012853d2e872fb56bc8a2e53718e2cafe1a4c823143141c6d90c322f"
+checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058"
[[package]]
name = "log"
@@ -458,28 +384,69 @@ dependencies = [
]
[[package]]
+name = "matchers"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558"
+dependencies = [
+ "regex-automata",
+]
+
+[[package]]
name = "memchr"
version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
[[package]]
+name = "nu-ansi-term"
+version = "0.46.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84"
+dependencies = [
+ "overload",
+ "winapi",
+]
+
+[[package]]
+name = "num-traits"
+version = "0.2.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c"
+dependencies = [
+ "autocfg",
+ "libm",
+]
+
+[[package]]
name = "once_cell"
version = "1.17.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
[[package]]
-name = "packed_simd_2"
-version = "0.3.7"
+name = "overload"
+version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "defdcfef86dcc44ad208f71d9ff4ce28df6537a4e0d6b0e8e845cb8ca10059a6"
+checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
+
+[[package]]
+name = "packed_simd"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1f9f08af0c877571712e2e3e686ad79efad9657dbf0f7c3c8ba943ff6c38932d"
dependencies = [
"cfg-if",
- "libm",
+ "num-traits",
]
[[package]]
+name = "pin-project-lite"
+version = "0.2.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116"
+
+[[package]]
name = "proc-macro2"
version = "1.0.63"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -519,9 +486,9 @@ dependencies = [
[[package]]
name = "regex"
-version = "1.5.5"
+version = "1.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1a11647b6b25ff05a515cb92c365cec08801e83423a235b51e231e1808747286"
+checksum = "8b1f693b24f6ac912f4893ef08244d70b6067480d2f1a46e950c9691e6749d1d"
dependencies = [
"aho-corasick",
"memchr",
@@ -529,10 +496,19 @@ dependencies = [
]
[[package]]
+name = "regex-automata"
+version = "0.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
+dependencies = [
+ "regex-syntax",
+]
+
+[[package]]
name = "regex-syntax"
-version = "0.6.25"
+version = "0.6.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
+checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
[[package]]
name = "rustfmt-config_proc_macro"
@@ -545,21 +521,20 @@ dependencies = [
[[package]]
name = "rustfmt-nightly"
-version = "1.6.0"
+version = "1.7.0"
dependencies = [
"annotate-snippets",
"anyhow",
"bytecount",
"cargo_metadata",
"clap",
+ "clap-cargo",
"diff",
"dirs",
- "env_logger",
"getopts",
"ignore",
"itertools",
"lazy_static",
- "log",
"regex",
"rustfmt-config_proc_macro",
"serde",
@@ -567,23 +542,11 @@ dependencies = [
"term",
"thiserror",
"toml",
+ "tracing",
+ "tracing-subscriber",
+ "unicode-properties",
"unicode-segmentation",
"unicode-width",
- "unicode_categories",
-]
-
-[[package]]
-name = "rustix"
-version = "0.37.11"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "85597d61f83914ddeba6a47b3b8ffe7365107221c2e557ed94426489fefb5f77"
-dependencies = [
- "bitflags",
- "errno",
- "io-lifetimes",
- "libc",
- "linux-raw-sys",
- "windows-sys 0.48.0",
]
[[package]]
@@ -657,6 +620,21 @@ dependencies = [
]
[[package]]
+name = "sharded-slab"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31"
+dependencies = [
+ "lazy_static",
+]
+
+[[package]]
+name = "smallvec"
+version = "1.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0"
+
+[[package]]
name = "strsim"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -685,15 +663,6 @@ dependencies = [
]
[[package]]
-name = "termcolor"
-version = "1.1.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755"
-dependencies = [
- "winapi-util",
-]
-
-[[package]]
name = "thiserror"
version = "1.0.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -757,28 +726,90 @@ dependencies = [
]
[[package]]
+name = "tracing"
+version = "0.1.37"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8"
+dependencies = [
+ "cfg-if",
+ "pin-project-lite",
+ "tracing-attributes",
+ "tracing-core",
+]
+
+[[package]]
+name = "tracing-attributes"
+version = "0.1.26"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "tracing-core"
+version = "0.1.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a"
+dependencies = [
+ "once_cell",
+ "valuable",
+]
+
+[[package]]
+name = "tracing-log"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922"
+dependencies = [
+ "lazy_static",
+ "log",
+ "tracing-core",
+]
+
+[[package]]
+name = "tracing-subscriber"
+version = "0.3.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "30a651bc37f915e81f087d86e62a18eec5f79550c7faff886f7090b4ea757c77"
+dependencies = [
+ "matchers",
+ "nu-ansi-term",
+ "once_cell",
+ "regex",
+ "sharded-slab",
+ "smallvec",
+ "thread_local",
+ "tracing",
+ "tracing-core",
+ "tracing-log",
+]
+
+[[package]]
name = "unicode-ident"
version = "1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4"
[[package]]
-name = "unicode-segmentation"
-version = "1.9.0"
+name = "unicode-properties"
+version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7e8820f5d777f6224dc4be3632222971ac30164d4a258d595640799554ebfd99"
+checksum = "c7f91c8b21fbbaa18853c3d0801c78f4fc94cdb976699bb03e832e75f7fd22f0"
[[package]]
-name = "unicode-width"
-version = "0.1.9"
+name = "unicode-segmentation"
+version = "1.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973"
+checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36"
[[package]]
-name = "unicode_categories"
-version = "0.1.1"
+name = "unicode-width"
+version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e"
+checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
[[package]]
name = "utf8parse"
@@ -787,6 +818,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
[[package]]
+name = "valuable"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
+
+[[package]]
name = "walkdir"
version = "2.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -836,35 +873,11 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-sys"
-version = "0.45.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
-dependencies = [
- "windows-targets 0.42.2",
-]
-
-[[package]]
-name = "windows-sys"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
dependencies = [
- "windows-targets 0.48.0",
-]
-
-[[package]]
-name = "windows-targets"
-version = "0.42.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
-dependencies = [
- "windows_aarch64_gnullvm 0.42.2",
- "windows_aarch64_msvc 0.42.2",
- "windows_i686_gnu 0.42.2",
- "windows_i686_msvc 0.42.2",
- "windows_x86_64_gnu 0.42.2",
- "windows_x86_64_gnullvm 0.42.2",
- "windows_x86_64_msvc 0.42.2",
+ "windows-targets",
]
[[package]]
@@ -873,95 +886,53 @@ version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5"
dependencies = [
- "windows_aarch64_gnullvm 0.48.0",
- "windows_aarch64_msvc 0.48.0",
- "windows_i686_gnu 0.48.0",
- "windows_i686_msvc 0.48.0",
- "windows_x86_64_gnu 0.48.0",
- "windows_x86_64_gnullvm 0.48.0",
- "windows_x86_64_msvc 0.48.0",
+ "windows_aarch64_gnullvm",
+ "windows_aarch64_msvc",
+ "windows_i686_gnu",
+ "windows_i686_msvc",
+ "windows_x86_64_gnu",
+ "windows_x86_64_gnullvm",
+ "windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
-version = "0.42.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
-
-[[package]]
-name = "windows_aarch64_gnullvm"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc"
[[package]]
name = "windows_aarch64_msvc"
-version = "0.42.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
-
-[[package]]
-name = "windows_aarch64_msvc"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3"
[[package]]
name = "windows_i686_gnu"
-version = "0.42.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
-
-[[package]]
-name = "windows_i686_gnu"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241"
[[package]]
name = "windows_i686_msvc"
-version = "0.42.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
-
-[[package]]
-name = "windows_i686_msvc"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00"
[[package]]
name = "windows_x86_64_gnu"
-version = "0.42.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
-
-[[package]]
-name = "windows_x86_64_gnu"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1"
[[package]]
name = "windows_x86_64_gnullvm"
-version = "0.42.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
-
-[[package]]
-name = "windows_x86_64_gnullvm"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953"
[[package]]
name = "windows_x86_64_msvc"
-version = "0.42.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
-
-[[package]]
-name = "windows_x86_64_msvc"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
diff --git a/src/tools/rustfmt/Cargo.toml b/src/tools/rustfmt/Cargo.toml
index 8c312f47a..00e0ed37a 100644
--- a/src/tools/rustfmt/Cargo.toml
+++ b/src/tools/rustfmt/Cargo.toml
@@ -1,11 +1,11 @@
[package]
name = "rustfmt-nightly"
-version = "1.6.0"
+version = "1.7.0"
description = "Tool to find and fix Rust formatting issues"
repository = "https://github.com/rust-lang/rustfmt"
readme = "README.md"
-license = "Apache-2.0/MIT"
+license = "Apache-2.0 OR MIT"
build = "build.rs"
categories = ["development-tools"]
edition = "2021"
@@ -35,26 +35,27 @@ generic-simd = ["bytecount/generic-simd"]
[dependencies]
annotate-snippets = { version = "0.9", features = ["color"] }
anyhow = "1.0"
-bytecount = "0.6"
+bytecount = "0.6.4"
cargo_metadata = "0.15.4"
-clap = { version = "4.2.1", features = ["derive"] }
+clap = { version = "4.4.2", features = ["derive"] }
+clap-cargo = "0.12.0"
diff = "0.1"
dirs = "4.0"
-env_logger = "0.10.0"
getopts = "0.2"
ignore = "0.4"
itertools = "0.10"
lazy_static = "1.4"
-log = "0.4"
-regex = "1.5"
+regex = "1.7"
serde = { version = "1.0.160", features = ["derive"] }
serde_json = "1.0"
term = "0.7"
thiserror = "1.0.40"
toml = "0.7.4"
+tracing = "0.1.37"
+tracing-subscriber = { version = "0.3.17", features = ["env-filter"] }
unicode-segmentation = "1.9"
unicode-width = "0.1"
-unicode_categories = "0.1"
+unicode-properties = { version = "0.1", default-features = false, features = ["general-category"] }
rustfmt-config_proc_macro = { version = "0.3", path = "config_proc_macro" }
diff --git a/src/tools/rustfmt/Contributing.md b/src/tools/rustfmt/Contributing.md
index b986a887c..69a2c7636 100644
--- a/src/tools/rustfmt/Contributing.md
+++ b/src/tools/rustfmt/Contributing.md
@@ -95,10 +95,18 @@ wish there weren't. You can leave `FIXME`s, preferably with an issue number.
You may want to run a version of rustfmt from source code as part of a test or
hacking on the rustfmt codebase. It's strongly discouraged to install a version
-of rustfmt from source. Instead, run it using `cargo run`, and `--manifest-path`.
+of rustfmt from source.
+
+To run `rustfmt` on a file:
+
+```
+cargo run --bin rustfmt -- path/to/file.rs
+```
+
+If you want to test modified `cargo-fmt`, or run `rustfmt` on the whole project (You may need to build rustfmt first):
```
-cargo run --bin cargo-fmt -- --manifest-path path/to/project/you/want2test/Cargo.toml
+RUSTFMT="./target/debug/rustfmt" cargo run --bin cargo-fmt -- --manifest-path path/to/project/you/want2test/Cargo.toml
```
### Version-gate formatting changes
diff --git a/src/tools/rustfmt/README.md b/src/tools/rustfmt/README.md
index c05184fbb..b68a942e4 100644
--- a/src/tools/rustfmt/README.md
+++ b/src/tools/rustfmt/README.md
@@ -229,4 +229,4 @@ See [LICENSE-APACHE](LICENSE-APACHE) and [LICENSE-MIT](LICENSE-MIT) for details.
[rust]: https://github.com/rust-lang/rust
[fmt rfcs]: https://github.com/rust-dev-tools/fmt-rfcs
-[style guide]: https://github.com/rust-dev-tools/fmt-rfcs/blob/master/guide/guide.md
+[style guide]: https://doc.rust-lang.org/nightly/style-guide/
diff --git a/src/tools/rustfmt/ci/build_and_test.bat b/src/tools/rustfmt/ci/build_and_test.bat
index 69dae1fff..16608a4aa 100755
--- a/src/tools/rustfmt/ci/build_and_test.bat
+++ b/src/tools/rustfmt/ci/build_and_test.bat
@@ -6,7 +6,11 @@ rustc -Vv || exit /b 1
cargo -V || exit /b 1
:: Build and test main crate
-cargo build --locked || exit /b 1
+if "%CFG_RELEASE_CHANNEL%"=="nightly" (
+ cargo build --locked --all-features || exit /b 1
+) else (
+ cargo build --locked || exit /b 1
+)
cargo test || exit /b 1
:: Build and test other crates
diff --git a/src/tools/rustfmt/ci/build_and_test.sh b/src/tools/rustfmt/ci/build_and_test.sh
index 949918532..207da362f 100755
--- a/src/tools/rustfmt/ci/build_and_test.sh
+++ b/src/tools/rustfmt/ci/build_and_test.sh
@@ -10,7 +10,11 @@ rustc -Vv
cargo -V
# Build and test main crate
-cargo build --locked
+if [ "$CFG_RELEASE_CHANNEL" == "nightly" ]; then
+ cargo build --locked --all-features
+else
+ cargo build --locked
+fi
cargo test
# Build and test other crates
diff --git a/src/tools/rustfmt/ci/check_diff.sh b/src/tools/rustfmt/ci/check_diff.sh
index 062c2dd86..50c58b1f4 100755
--- a/src/tools/rustfmt/ci/check_diff.sh
+++ b/src/tools/rustfmt/ci/check_diff.sh
@@ -1,5 +1,10 @@
#!/bin/bash
+set -e
+
+# https://github.com/rust-lang/rustfmt/issues/5675
+export LD_LIBRARY_PATH=$(rustc --print sysroot)/lib:$LD_LIBRARY_PATH
+
function print_usage() {
echo "usage check_diff REMOTE_REPO FEATURE_BRANCH [COMMIT_HASH] [OPTIONAL_RUSTFMT_CONFIGS]"
}
@@ -110,7 +115,7 @@ function compile_rustfmt() {
git fetch feature $FEATURE_BRANCH
cargo build --release --bin rustfmt && cp target/release/rustfmt $1/rustfmt
- if [ -z "$OPTIONAL_COMMIT_HASH" ]; then
+ if [ -z "$OPTIONAL_COMMIT_HASH" ] || [ "$FEATURE_BRANCH" = "$OPTIONAL_COMMIT_HASH" ]; then
git switch $FEATURE_BRANCH
else
git switch $OPTIONAL_COMMIT_HASH --detach
@@ -140,9 +145,15 @@ function check_repo() {
init_submodules $SUBMODULES
fi
+
+ # rustfmt --check returns 1 if a diff was found
+ # Also check_diff returns 1 if there was a diff between master rustfmt and the feature branch
+ # so we want to ignore the exit status check
+ set +e
check_diff $REPO_NAME
# append the status of running `check_diff` to the STATUSES array
STATUSES+=($?)
+ set -e
echo "removing tmp_dir $tmp_dir"
rm -rf $tmp_dir
diff --git a/src/tools/rustfmt/config_proc_macro/Cargo.toml b/src/tools/rustfmt/config_proc_macro/Cargo.toml
index 34e8c237f..eda8a7fce 100644
--- a/src/tools/rustfmt/config_proc_macro/Cargo.toml
+++ b/src/tools/rustfmt/config_proc_macro/Cargo.toml
@@ -3,7 +3,7 @@ name = "rustfmt-config_proc_macro"
version = "0.3.0"
edition = "2018"
description = "A collection of procedural macros for rustfmt"
-license = "Apache-2.0/MIT"
+license = "Apache-2.0 OR MIT"
categories = ["development-tools::procedural-macro-helpers"]
repository = "https://github.com/rust-lang/rustfmt"
diff --git a/src/tools/rustfmt/rust-toolchain b/src/tools/rustfmt/rust-toolchain
index 33ff8b03d..0057e2f37 100644
--- a/src/tools/rustfmt/rust-toolchain
+++ b/src/tools/rustfmt/rust-toolchain
@@ -1,3 +1,3 @@
[toolchain]
-channel = "nightly-2023-07-01"
+channel = "nightly-2023-10-22"
components = ["llvm-tools", "rustc-dev"]
diff --git a/src/tools/rustfmt/src/attr.rs b/src/tools/rustfmt/src/attr.rs
index 22e45082a..4d83547d6 100644
--- a/src/tools/rustfmt/src/attr.rs
+++ b/src/tools/rustfmt/src/attr.rs
@@ -308,7 +308,7 @@ impl Rewrite for ast::MetaItem {
// See #2479 for example.
let value = rewrite_literal(context, lit.as_token_lit(), lit.span, lit_shape)
.unwrap_or_else(|| context.snippet(lit.span).to_owned());
- format!("{} = {}", path, value)
+ format!("{path} = {value}")
}
})
}
@@ -342,7 +342,7 @@ impl Rewrite for ast::Attribute {
let literal_str = literal.as_str();
let doc_comment_formatter =
DocCommentFormatter::new(literal_str, comment_style);
- let doc_comment = format!("{}", doc_comment_formatter);
+ let doc_comment = format!("{doc_comment_formatter}");
return rewrite_doc_comment(
&doc_comment,
shape.comment(context.config),
@@ -406,9 +406,9 @@ impl Rewrite for [ast::Attribute] {
0,
)?;
let comment = if comment.is_empty() {
- format!("\n{}", mlb)
+ format!("\n{mlb}")
} else {
- format!("{}{}\n{}", mla, comment, mlb)
+ format!("{mla}{comment}\n{mlb}")
};
result.push_str(&comment);
result.push_str(&shape.indent.to_string(context.config));
diff --git a/src/tools/rustfmt/src/attr/doc_comment.rs b/src/tools/rustfmt/src/attr/doc_comment.rs
index 25c8158df..f55201839 100644
--- a/src/tools/rustfmt/src/attr/doc_comment.rs
+++ b/src/tools/rustfmt/src/attr/doc_comment.rs
@@ -20,15 +20,15 @@ impl Display for DocCommentFormatter<'_> {
// Handle `#[doc = ""]`.
if lines.peek().is_none() {
- return write!(formatter, "{}", opener);
+ return write!(formatter, "{opener}");
}
while let Some(line) = lines.next() {
let is_last_line = lines.peek().is_none();
if is_last_line {
- write!(formatter, "{}{}", opener, line)?;
+ write!(formatter, "{opener}{line}")?;
} else {
- writeln!(formatter, "{}{}", opener, line)?;
+ writeln!(formatter, "{opener}{line}")?;
}
}
Ok(())
diff --git a/src/tools/rustfmt/src/bin/main.rs b/src/tools/rustfmt/src/bin/main.rs
index 03b75c1b0..6f5640836 100644
--- a/src/tools/rustfmt/src/bin/main.rs
+++ b/src/tools/rustfmt/src/bin/main.rs
@@ -6,6 +6,7 @@ use io::Error as IoError;
use thiserror::Error;
use rustfmt_nightly as rustfmt;
+use tracing_subscriber::EnvFilter;
use std::collections::HashMap;
use std::env;
@@ -29,13 +30,15 @@ extern crate rustc_driver;
fn main() {
rustc_driver::install_ice_hook(BUG_REPORT_URL, |_| ());
- env_logger::Builder::from_env("RUSTFMT_LOG").init();
+ tracing_subscriber::fmt()
+ .with_env_filter(EnvFilter::from_env("RUSTFMT_LOG"))
+ .init();
let opts = make_opts();
let exit_code = match execute(&opts) {
Ok(code) => code,
Err(e) => {
- eprintln!("{:#}", e);
+ eprintln!("{e:#}");
1
}
};
@@ -281,7 +284,7 @@ fn format_string(input: String, options: GetOptsOptions) -> Result<i32> {
for f in config.file_lines().files() {
match *f {
FileName::Stdin => {}
- _ => eprintln!("Warning: Extra file listed in file_lines option '{}'", f),
+ _ => eprintln!("Warning: Extra file listed in file_lines option '{f}'"),
}
}
@@ -377,7 +380,7 @@ fn format_and_emit_report<T: Write>(session: &mut Session<'_, T>, input: Input)
}
}
Err(msg) => {
- eprintln!("Error writing files: {}", msg);
+ eprintln!("Error writing files: {msg}");
session.add_operational_error();
}
}
@@ -400,12 +403,9 @@ fn print_usage_to_stdout(opts: &Options, reason: &str) {
let sep = if reason.is_empty() {
String::new()
} else {
- format!("{}\n\n", reason)
+ format!("{reason}\n\n")
};
- let msg = format!(
- "{}Format Rust code\n\nusage: rustfmt [options] <file>...",
- sep
- );
+ let msg = format!("{sep}Format Rust code\n\nusage: rustfmt [options] <file>...");
println!("{}", opts.usage(&msg));
}
@@ -419,7 +419,7 @@ are 1-based and inclusive of both end points. Specifying an empty array
will result in no files being formatted. For example,
```
-rustfmt --file-lines '[
+rustfmt src/lib.rs src/foo.rs --file-lines '[
{{\"file\":\"src/lib.rs\",\"range\":[7,13]}},
{{\"file\":\"src/lib.rs\",\"range\":[21,29]}},
{{\"file\":\"src/foo.rs\",\"range\":[10,11]}},
@@ -439,7 +439,7 @@ fn print_version() {
include_str!(concat!(env!("OUT_DIR"), "/commit-info.txt"))
);
- println!("rustfmt {}", version_info);
+ println!("rustfmt {version_info}");
}
fn determine_operation(matches: &Matches) -> Result<Operation, OperationError> {
@@ -644,9 +644,9 @@ impl GetOptsOptions {
match *f {
FileName::Real(ref f) if files.contains(f) => {}
FileName::Real(_) => {
- eprintln!("Warning: Extra file listed in file_lines option '{}'", f)
+ eprintln!("Warning: Extra file listed in file_lines option '{f}'")
}
- FileName::Stdin => eprintln!("Warning: Not a file '{}'", f),
+ FileName::Stdin => eprintln!("Warning: Not a file '{f}'"),
}
}
}
diff --git a/src/tools/rustfmt/src/cargo-fmt/main.rs b/src/tools/rustfmt/src/cargo-fmt/main.rs
index bc9745275..a1ad1aafa 100644
--- a/src/tools/rustfmt/src/cargo-fmt/main.rs
+++ b/src/tools/rustfmt/src/cargo-fmt/main.rs
@@ -22,27 +22,28 @@ use clap::{CommandFactory, Parser};
mod cargo_fmt_tests;
#[derive(Parser)]
-#[clap(
+#[command(
disable_version_flag = true,
bin_name = "cargo fmt",
about = "This utility formats all bin and lib files of \
the current crate using rustfmt."
)]
+#[command(styles = clap_cargo::style::CLAP_STYLING)]
pub struct Opts {
/// No output printed to stdout
- #[clap(short = 'q', long = "quiet")]
+ #[arg(short = 'q', long = "quiet")]
quiet: bool,
/// Use verbose output
- #[clap(short = 'v', long = "verbose")]
+ #[arg(short = 'v', long = "verbose")]
verbose: bool,
/// Print rustfmt version and exit
- #[clap(long = "version")]
+ #[arg(long = "version")]
version: bool,
/// Specify package to format
- #[clap(
+ #[arg(
short = 'p',
long = "package",
value_name = "package",
@@ -51,24 +52,24 @@ pub struct Opts {
packages: Vec<String>,
/// Specify path to Cargo.toml
- #[clap(long = "manifest-path", value_name = "manifest-path")]
+ #[arg(long = "manifest-path", value_name = "manifest-path")]
manifest_path: Option<String>,
/// Specify message-format: short|json|human
- #[clap(long = "message-format", value_name = "message-format")]
+ #[arg(long = "message-format", value_name = "message-format")]
message_format: Option<String>,
/// Options passed to rustfmt
// 'raw = true' to make `--` explicit.
- #[clap(name = "rustfmt_options", raw(true))]
+ #[arg(name = "rustfmt_options", raw = true)]
rustfmt_options: Vec<String>,
/// Format all packages, and also their local path-based dependencies
- #[clap(long = "all")]
+ #[arg(long = "all")]
format_all: bool,
/// Run rustfmt in check mode
- #[clap(long = "check")]
+ #[arg(long = "check")]
check: bool,
}
@@ -200,14 +201,13 @@ fn convert_message_format_to_rustfmt_args(
}
"human" => Ok(()),
_ => Err(format!(
- "invalid --message-format value: {}. Allowed values are: short|json|human",
- message_format
+ "invalid --message-format value: {message_format}. Allowed values are: short|json|human"
)),
}
}
fn print_usage_to_stderr(reason: &str) {
- eprintln!("{}", reason);
+ eprintln!("{reason}");
let app = Opts::command();
app.after_help("")
.write_help(&mut io::stderr())
@@ -460,7 +460,7 @@ fn get_targets_with_hitlist(
let package = workspace_hitlist.iter().next().unwrap();
Err(io::Error::new(
io::ErrorKind::InvalidInput,
- format!("package `{}` is not a member of the workspace", package),
+ format!("package `{package}` is not a member of the workspace"),
))
}
}
@@ -498,7 +498,7 @@ fn run_rustfmt(
if verbosity == Verbosity::Verbose {
print!("rustfmt");
- print!(" --edition {}", edition);
+ print!(" --edition {edition}");
fmt_args.iter().for_each(|f| print!(" {}", f));
files.iter().for_each(|f| print!(" {}", f.display()));
println!();
diff --git a/src/tools/rustfmt/src/chains.rs b/src/tools/rustfmt/src/chains.rs
index 0afce7cf6..ea23690ca 100644
--- a/src/tools/rustfmt/src/chains.rs
+++ b/src/tools/rustfmt/src/chains.rs
@@ -153,7 +153,13 @@ enum CommentPosition {
Top,
}
-// An expression plus trailing `?`s to be formatted together.
+/// Information about an expression in a chain.
+struct SubExpr {
+ expr: ast::Expr,
+ is_method_call_receiver: bool,
+}
+
+/// An expression plus trailing `?`s to be formatted together.
#[derive(Debug)]
struct ChainItem {
kind: ChainItemKind,
@@ -166,7 +172,10 @@ struct ChainItem {
// would remove a lot of cloning.
#[derive(Debug)]
enum ChainItemKind {
- Parent(ast::Expr),
+ Parent {
+ expr: ast::Expr,
+ parens: bool,
+ },
MethodCall(
ast::PathSegment,
Vec<ast::GenericArg>,
@@ -181,7 +190,7 @@ enum ChainItemKind {
impl ChainItemKind {
fn is_block_like(&self, context: &RewriteContext<'_>, reps: &str) -> bool {
match self {
- ChainItemKind::Parent(ref expr) => utils::is_block_expr(context, expr, reps),
+ ChainItemKind::Parent { expr, .. } => utils::is_block_expr(context, expr, reps),
ChainItemKind::MethodCall(..)
| ChainItemKind::StructField(..)
| ChainItemKind::TupleField(..)
@@ -199,7 +208,11 @@ impl ChainItemKind {
}
}
- fn from_ast(context: &RewriteContext<'_>, expr: &ast::Expr) -> (ChainItemKind, Span) {
+ fn from_ast(
+ context: &RewriteContext<'_>,
+ expr: &ast::Expr,
+ is_method_call_receiver: bool,
+ ) -> (ChainItemKind, Span) {
let (kind, span) = match expr.kind {
ast::ExprKind::MethodCall(ref call) => {
let types = if let Some(ref generic_args) = call.seg.args {
@@ -236,7 +249,15 @@ impl ChainItemKind {
let span = mk_sp(nested.span.hi(), expr.span.hi());
(ChainItemKind::Await, span)
}
- _ => return (ChainItemKind::Parent(expr.clone()), expr.span),
+ _ => {
+ return (
+ ChainItemKind::Parent {
+ expr: expr.clone(),
+ parens: is_method_call_receiver && should_add_parens(expr),
+ },
+ expr.span,
+ );
+ }
};
// Remove comments from the span.
@@ -249,7 +270,14 @@ impl Rewrite for ChainItem {
fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
let shape = shape.sub_width(self.tries)?;
let rewrite = match self.kind {
- ChainItemKind::Parent(ref expr) => expr.rewrite(context, shape)?,
+ ChainItemKind::Parent {
+ ref expr,
+ parens: true,
+ } => crate::expr::rewrite_paren(context, &expr, shape, expr.span)?,
+ ChainItemKind::Parent {
+ ref expr,
+ parens: false,
+ } => expr.rewrite(context, shape)?,
ChainItemKind::MethodCall(ref segment, ref types, ref exprs) => {
Self::rewrite_method_call(segment.ident, types, exprs, self.span, context, shape)?
}
@@ -268,13 +296,14 @@ impl Rewrite for ChainItem {
rewrite_comment(comment, false, shape, context.config)?
}
};
- Some(format!("{}{}", rewrite, "?".repeat(self.tries)))
+ Some(format!("{rewrite}{}", "?".repeat(self.tries)))
}
}
impl ChainItem {
- fn new(context: &RewriteContext<'_>, expr: &ast::Expr, tries: usize) -> ChainItem {
- let (kind, span) = ChainItemKind::from_ast(context, expr);
+ fn new(context: &RewriteContext<'_>, expr: &SubExpr, tries: usize) -> ChainItem {
+ let (kind, span) =
+ ChainItemKind::from_ast(context, &expr.expr, expr.is_method_call_receiver);
ChainItem { kind, tries, span }
}
@@ -327,7 +356,7 @@ impl Chain {
let mut rev_children = vec![];
let mut sub_tries = 0;
for subexpr in &subexpr_list {
- match subexpr.kind {
+ match subexpr.expr.kind {
ast::ExprKind::Try(_) => sub_tries += 1,
_ => {
rev_children.push(ChainItem::new(context, subexpr, sub_tries));
@@ -442,11 +471,14 @@ impl Chain {
// Returns a Vec of the prefixes of the chain.
// E.g., for input `a.b.c` we return [`a.b.c`, `a.b`, 'a']
- fn make_subexpr_list(expr: &ast::Expr, context: &RewriteContext<'_>) -> Vec<ast::Expr> {
- let mut subexpr_list = vec![expr.clone()];
+ fn make_subexpr_list(expr: &ast::Expr, context: &RewriteContext<'_>) -> Vec<SubExpr> {
+ let mut subexpr_list = vec![SubExpr {
+ expr: expr.clone(),
+ is_method_call_receiver: false,
+ }];
while let Some(subexpr) = Self::pop_expr_chain(subexpr_list.last().unwrap(), context) {
- subexpr_list.push(subexpr.clone());
+ subexpr_list.push(subexpr);
}
subexpr_list
@@ -454,12 +486,18 @@ impl Chain {
// Returns the expression's subexpression, if it exists. When the subexpr
// is a try! macro, we'll convert it to shorthand when the option is set.
- fn pop_expr_chain(expr: &ast::Expr, context: &RewriteContext<'_>) -> Option<ast::Expr> {
- match expr.kind {
- ast::ExprKind::MethodCall(ref call) => Some(Self::convert_try(&call.receiver, context)),
+ fn pop_expr_chain(expr: &SubExpr, context: &RewriteContext<'_>) -> Option<SubExpr> {
+ match expr.expr.kind {
+ ast::ExprKind::MethodCall(ref call) => Some(SubExpr {
+ expr: Self::convert_try(&call.receiver, context),
+ is_method_call_receiver: true,
+ }),
ast::ExprKind::Field(ref subexpr, _)
| ast::ExprKind::Try(ref subexpr)
- | ast::ExprKind::Await(ref subexpr, _) => Some(Self::convert_try(subexpr, context)),
+ | ast::ExprKind::Await(ref subexpr, _) => Some(SubExpr {
+ expr: Self::convert_try(subexpr, context),
+ is_method_call_receiver: false,
+ }),
_ => None,
}
}
@@ -940,3 +978,22 @@ fn trim_tries(s: &str) -> String {
}
result
}
+
+/// Whether a method call's receiver needs parenthesis, like
+/// ```rust,ignore
+/// || .. .method();
+/// || 1.. .method();
+/// 1. .method();
+/// ```
+/// Which all need parenthesis or a space before `.method()`.
+fn should_add_parens(expr: &ast::Expr) -> bool {
+ match expr.kind {
+ ast::ExprKind::Lit(ref lit) => crate::expr::lit_ends_in_dot(lit),
+ ast::ExprKind::Closure(ref cl) => match cl.body.kind {
+ ast::ExprKind::Range(_, _, ast::RangeLimits::HalfOpen) => true,
+ ast::ExprKind::Lit(ref lit) => crate::expr::lit_ends_in_dot(lit),
+ _ => false,
+ },
+ _ => false,
+ }
+}
diff --git a/src/tools/rustfmt/src/closures.rs b/src/tools/rustfmt/src/closures.rs
index c95e9a97b..8a4089a56 100644
--- a/src/tools/rustfmt/src/closures.rs
+++ b/src/tools/rustfmt/src/closures.rs
@@ -12,7 +12,7 @@ use crate::overflow::OverflowableItem;
use crate::rewrite::{Rewrite, RewriteContext};
use crate::shape::Shape;
use crate::source_map::SpanUtils;
-use crate::types::rewrite_lifetime_param;
+use crate::types::rewrite_bound_params;
use crate::utils::{last_line_width, left_most_sub_expr, stmt_expr, NodeIdExt};
// This module is pretty messy because of the rules around closures and blocks:
@@ -175,7 +175,7 @@ fn rewrite_closure_with_block(
shape,
false,
)?;
- Some(format!("{} {}", prefix, block))
+ Some(format!("{prefix} {block}"))
}
// Rewrite closure with a single expression without wrapping its body with block.
@@ -188,7 +188,7 @@ fn rewrite_closure_expr(
fn allow_multi_line(expr: &ast::Expr) -> bool {
match expr.kind {
ast::ExprKind::Match(..)
- | ast::ExprKind::Async(..)
+ | ast::ExprKind::Gen(..)
| ast::ExprKind::Block(..)
| ast::ExprKind::TryBlock(..)
| ast::ExprKind::Loop(..)
@@ -246,7 +246,7 @@ fn rewrite_closure_fn_decl(
"for<> ".to_owned()
}
ast::ClosureBinder::For { generic_params, .. } => {
- let lifetime_str = rewrite_lifetime_param(context, shape, generic_params)?;
+ let lifetime_str = rewrite_bound_params(context, shape, generic_params)?;
format!("for<{lifetime_str}> ")
}
ast::ClosureBinder::NotPresent => "".to_owned(),
@@ -264,7 +264,7 @@ fn rewrite_closure_fn_decl(
""
};
let is_async = if asyncness.is_async() { "async " } else { "" };
- let mover = if capture == ast::CaptureBy::Value {
+ let mover = if matches!(capture, ast::CaptureBy::Value { .. }) {
"move "
} else {
""
@@ -310,10 +310,7 @@ fn rewrite_closure_fn_decl(
.tactic(tactic)
.preserve_newline(true);
let list_str = write_list(&item_vec, &fmt)?;
- let mut prefix = format!(
- "{}{}{}{}{}|{}|",
- binder, const_, immovable, is_async, mover, list_str
- );
+ let mut prefix = format!("{binder}{const_}{immovable}{is_async}{mover}|{list_str}|");
if !ret_str.is_empty() {
if prefix.contains('\n') {
diff --git a/src/tools/rustfmt/src/comment.rs b/src/tools/rustfmt/src/comment.rs
index 85918ecc1..7da0f79bd 100644
--- a/src/tools/rustfmt/src/comment.rs
+++ b/src/tools/rustfmt/src/comment.rs
@@ -58,25 +58,23 @@ fn custom_opener(s: &str) -> &str {
}
impl<'a> CommentStyle<'a> {
- /// Returns `true` if the commenting style covers a line only.
+ /// Returns `true` if the commenting style cannot span multiple lines.
pub(crate) fn is_line_comment(&self) -> bool {
- match *self {
+ matches!(
+ self,
CommentStyle::DoubleSlash
- | CommentStyle::TripleSlash
- | CommentStyle::Doc
- | CommentStyle::Custom(_) => true,
- _ => false,
- }
+ | CommentStyle::TripleSlash
+ | CommentStyle::Doc
+ | CommentStyle::Custom(_)
+ )
}
- /// Returns `true` if the commenting style can span over multiple lines.
+ /// Returns `true` if the commenting style can span multiple lines.
pub(crate) fn is_block_comment(&self) -> bool {
- match *self {
- CommentStyle::SingleBullet | CommentStyle::DoubleBullet | CommentStyle::Exclamation => {
- true
- }
- _ => false,
- }
+ matches!(
+ self,
+ CommentStyle::SingleBullet | CommentStyle::DoubleBullet | CommentStyle::Exclamation
+ )
}
/// Returns `true` if the commenting style is for documentation.
@@ -367,7 +365,11 @@ fn identify_comment(
trim_left_preserve_layout(first_group, shape.indent, config)?
} else if !config.normalize_comments()
&& !config.wrap_comments()
- && !config.format_code_in_doc_comments()
+ && !(
+ // `format_code_in_doc_comments` should only take effect on doc comments,
+ // so we only consider it when this comment block is a doc comment block.
+ is_doc_comment && config.format_code_in_doc_comments()
+ )
{
light_rewrite_comment(first_group, shape.indent, config, is_doc_comment)
} else {
@@ -484,7 +486,9 @@ impl ItemizedBlock {
// allowed.
for suffix in [". ", ") "] {
if let Some((prefix, _)) = trimmed.split_once(suffix) {
- if prefix.len() <= 2 && prefix.chars().all(|c| char::is_ascii_digit(&c)) {
+ let has_leading_digits = (1..=2).contains(&prefix.len())
+ && prefix.chars().all(|c| char::is_ascii_digit(&c));
+ if has_leading_digits {
return Some(prefix.len() + suffix.len());
}
}
@@ -623,7 +627,7 @@ impl<'a> CommentRewrite<'a> {
is_prev_line_multi_line: false,
code_block_attr: None,
item_block: None,
- comment_line_separator: format!("{}{}", indent_str, line_start),
+ comment_line_separator: format!("{indent_str}{line_start}"),
max_width,
indent_str,
fmt_indent: shape.indent,
@@ -953,7 +957,7 @@ const RUSTFMT_CUSTOM_COMMENT_PREFIX: &str = "//#### ";
fn hide_sharp_behind_comment(s: &str) -> Cow<'_, str> {
let s_trimmed = s.trim();
if s_trimmed.starts_with("# ") || s_trimmed == "#" {
- Cow::from(format!("{}{}", RUSTFMT_CUSTOM_COMMENT_PREFIX, s))
+ Cow::from(format!("{RUSTFMT_CUSTOM_COMMENT_PREFIX}{s}"))
} else {
Cow::from(s)
}
@@ -1037,7 +1041,7 @@ pub(crate) fn recover_missing_comment_in_span(
} else {
Cow::from(" ")
};
- Some(format!("{}{}", sep, missing_comment))
+ Some(format!("{sep}{missing_comment}"))
}
}
@@ -1834,8 +1838,7 @@ fn remove_comment_header(comment: &str) -> &str {
} else {
assert!(
comment.starts_with("/*"),
- "string '{}' is not a comment",
- comment
+ "string '{comment}' is not a comment"
);
&comment[2..comment.len() - 2]
}
@@ -2071,26 +2074,13 @@ fn main() {
expected_line_start: &str,
) {
let block = ItemizedBlock::new(test_input).unwrap();
- assert_eq!(1, block.lines.len(), "test_input: {:?}", test_input);
- assert_eq!(
- expected_line, &block.lines[0],
- "test_input: {:?}",
- test_input
- );
- assert_eq!(
- expected_indent, block.indent,
- "test_input: {:?}",
- test_input
- );
- assert_eq!(
- expected_opener, &block.opener,
- "test_input: {:?}",
- test_input
- );
+ assert_eq!(1, block.lines.len(), "test_input: {test_input:?}");
+ assert_eq!(expected_line, &block.lines[0], "test_input: {test_input:?}");
+ assert_eq!(expected_indent, block.indent, "test_input: {test_input:?}");
+ assert_eq!(expected_opener, &block.opener, "test_input: {test_input:?}");
assert_eq!(
expected_line_start, &block.line_start,
- "test_input: {:?}",
- test_input
+ "test_input: {test_input:?}"
);
}
@@ -2142,13 +2132,15 @@ fn main() {
// https://spec.commonmark.org/0.30 says: "A start number may not be negative":
"-1. Not a list item.",
"-1 Not a list item.",
+ // Marker without prefix are not recognized as item markers:
+ ". Not a list item.",
+ ") Not a list item.",
];
for line in test_inputs.iter() {
let maybe_block = ItemizedBlock::new(line);
assert!(
maybe_block.is_none(),
- "The following line shouldn't be classified as a list item: {}",
- line
+ "The following line shouldn't be classified as a list item: {line}"
);
}
}
diff --git a/src/tools/rustfmt/src/config/config_type.rs b/src/tools/rustfmt/src/config/config_type.rs
index c836b4bbb..feb452d72 100644
--- a/src/tools/rustfmt/src/config/config_type.rs
+++ b/src/tools/rustfmt/src/config/config_type.rs
@@ -500,18 +500,16 @@ where
// Stable with an unstable option
(false, false, _) => {
eprintln!(
- "Warning: can't set `{} = {:?}`, unstable features are only \
- available in nightly channel.",
- option_name, option_value
+ "Warning: can't set `{option_name} = {option_value:?}`, unstable features are only \
+ available in nightly channel."
);
false
}
// Stable with a stable option, but an unstable variant
(false, true, false) => {
eprintln!(
- "Warning: can't set `{} = {:?}`, unstable variants are only \
- available in nightly channel.",
- option_name, option_value
+ "Warning: can't set `{option_name} = {option_value:?}`, unstable variants are only \
+ available in nightly channel."
);
false
}
diff --git a/src/tools/rustfmt/src/config/file_lines.rs b/src/tools/rustfmt/src/config/file_lines.rs
index e4e51a3f3..e33fe9bb2 100644
--- a/src/tools/rustfmt/src/config/file_lines.rs
+++ b/src/tools/rustfmt/src/config/file_lines.rs
@@ -162,7 +162,7 @@ impl fmt::Display for FileLines {
None => write!(f, "None")?,
Some(map) => {
for (file_name, ranges) in map.iter() {
- write!(f, "{}: ", file_name)?;
+ write!(f, "{file_name}: ")?;
write!(f, "{}\n", ranges.iter().format(", "))?;
}
}
diff --git a/src/tools/rustfmt/src/config/macro_names.rs b/src/tools/rustfmt/src/config/macro_names.rs
index 26ad78d6d..edfe925c2 100644
--- a/src/tools/rustfmt/src/config/macro_names.rs
+++ b/src/tools/rustfmt/src/config/macro_names.rs
@@ -3,7 +3,7 @@
use itertools::Itertools;
use std::{fmt, str};
-use serde::{Deserialize, Serialize};
+use serde::{Deserialize, Deserializer, Serialize};
use serde_json as json;
use thiserror::Error;
@@ -30,12 +30,22 @@ impl From<MacroName> for String {
}
/// Defines a selector to match against a macro.
-#[derive(Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd, Deserialize, Serialize)]
+#[derive(Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd, Serialize)]
pub enum MacroSelector {
Name(MacroName),
All,
}
+impl<'de> Deserialize<'de> for MacroSelector {
+ fn deserialize<D>(de: D) -> Result<Self, D::Error>
+ where
+ D: Deserializer<'de>,
+ {
+ let s = String::deserialize(de)?;
+ std::str::FromStr::from_str(&s).map_err(serde::de::Error::custom)
+ }
+}
+
impl fmt::Display for MacroSelector {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
@@ -113,6 +123,6 @@ mod test {
#[test]
fn macro_names_display() {
let macro_names = MacroSelectors::from_str(r#"["foo", "*", "bar"]"#).unwrap();
- assert_eq!(format!("{}", macro_names), "foo, *, bar");
+ assert_eq!(format!("{macro_names}"), "foo, *, bar");
}
}
diff --git a/src/tools/rustfmt/src/config/mod.rs b/src/tools/rustfmt/src/config/mod.rs
index 6f41b299e..7538b2652 100644
--- a/src/tools/rustfmt/src/config/mod.rs
+++ b/src/tools/rustfmt/src/config/mod.rs
@@ -216,8 +216,8 @@ impl Config {
let required_version = self.required_version();
if version != required_version {
println!(
- "Error: rustfmt version ({}) doesn't match the required version ({})",
- version, required_version,
+ "Error: rustfmt version ({version}) doesn't match the required version \
+({required_version})"
);
return false;
}
@@ -310,20 +310,20 @@ impl Config {
.ok_or_else(|| String::from("Parsed config was not table"))?;
for key in table.keys() {
if !Config::is_valid_name(key) {
- let msg = &format!("Warning: Unknown configuration option `{}`\n", key);
+ let msg = &format!("Warning: Unknown configuration option `{key}`\n");
err.push_str(msg)
}
}
match parsed.try_into() {
Ok(parsed_config) => {
if !err.is_empty() {
- eprint!("{}", err);
+ eprint!("{err}");
}
Ok(Config::default().fill_from_parsed_config(parsed_config, dir))
}
Err(e) => {
err.push_str("Error: Decoding config file failed:\n");
- err.push_str(format!("{}\n", e).as_str());
+ err.push_str(format!("{e}\n").as_str());
err.push_str("Please check your config file.");
Err(err)
}
@@ -563,10 +563,7 @@ mod test {
let toml = used_options.to_toml().unwrap();
assert_eq!(
toml,
- format!(
- "merge_derives = {}\nskip_children = {}\n",
- merge_derives, skip_children,
- )
+ format!("merge_derives = {merge_derives}\nskip_children = {skip_children}\n",)
);
}
diff --git a/src/tools/rustfmt/src/config/options.rs b/src/tools/rustfmt/src/config/options.rs
index 3aa1a4de9..03fd91eb1 100644
--- a/src/tools/rustfmt/src/config/options.rs
+++ b/src/tools/rustfmt/src/config/options.rs
@@ -1,3 +1,5 @@
+#![allow(unused_imports)]
+
use std::collections::{hash_set, HashSet};
use std::fmt;
use std::path::{Path, PathBuf};
@@ -243,7 +245,7 @@ pub struct WidthHeuristics {
impl fmt::Display for WidthHeuristics {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- write!(f, "{:?}", self)
+ write!(f, "{self:?}")
}
}
diff --git a/src/tools/rustfmt/src/emitter.rs b/src/tools/rustfmt/src/emitter.rs
index dc2c99a30..9c335314d 100644
--- a/src/tools/rustfmt/src/emitter.rs
+++ b/src/tools/rustfmt/src/emitter.rs
@@ -47,6 +47,6 @@ pub(crate) trait Emitter {
fn ensure_real_path(filename: &FileName) -> &Path {
match *filename {
FileName::Real(ref path) => path,
- _ => panic!("cannot format `{}` and emit to files", filename),
+ _ => panic!("cannot format `{filename}` and emit to files"),
}
}
diff --git a/src/tools/rustfmt/src/emitter/checkstyle.rs b/src/tools/rustfmt/src/emitter/checkstyle.rs
index 545b25997..56d6a0ed6 100644
--- a/src/tools/rustfmt/src/emitter/checkstyle.rs
+++ b/src/tools/rustfmt/src/emitter/checkstyle.rs
@@ -43,7 +43,7 @@ pub(crate) fn output_checkstyle_file<T>(
where
T: Write,
{
- write!(writer, r#"<file name="{}">"#, filename)?;
+ write!(writer, r#"<file name="{filename}">"#)?;
for mismatch in diff {
let begin_line = mismatch.line_number;
let mut current_line;
@@ -82,7 +82,7 @@ mod tests {
);
assert_eq!(
&writer[..],
- format!(r#"<file name="{}"></file>"#, file_name).as_bytes()
+ format!(r#"<file name="{file_name}"></file>"#).as_bytes()
);
}
diff --git a/src/tools/rustfmt/src/emitter/checkstyle/xml.rs b/src/tools/rustfmt/src/emitter/checkstyle/xml.rs
index f251aabe8..d1d9af708 100644
--- a/src/tools/rustfmt/src/emitter/checkstyle/xml.rs
+++ b/src/tools/rustfmt/src/emitter/checkstyle/xml.rs
@@ -13,7 +13,7 @@ impl<'a> Display for XmlEscaped<'a> {
'"' => write!(formatter, "&quot;"),
'\'' => write!(formatter, "&apos;"),
'&' => write!(formatter, "&amp;"),
- _ => write!(formatter, "{}", char),
+ _ => write!(formatter, "{char}"),
}?;
}
diff --git a/src/tools/rustfmt/src/emitter/diff.rs b/src/tools/rustfmt/src/emitter/diff.rs
index 5e1f13446..764cd136e 100644
--- a/src/tools/rustfmt/src/emitter/diff.rs
+++ b/src/tools/rustfmt/src/emitter/diff.rs
@@ -28,7 +28,7 @@ impl Emitter for DiffEmitter {
if has_diff {
if self.config.print_misformatted_file_names() {
- writeln!(output, "{}", filename)?;
+ writeln!(output, "{filename}")?;
} else {
print_diff(
mismatch,
@@ -40,7 +40,7 @@ impl Emitter for DiffEmitter {
// This occurs when the only difference between the original and formatted values
// is the newline style. This happens because The make_diff function compares the
// original and formatted values line by line, independent of line endings.
- writeln!(output, "Incorrect newline style in {}", filename)?;
+ writeln!(output, "Incorrect newline style in {filename}")?;
return Ok(EmitterResult { has_diff: true });
}
@@ -110,7 +110,7 @@ mod tests {
assert_eq!(
String::from_utf8(writer).unwrap(),
- format!("{}\n{}\n", bin_file, lib_file),
+ format!("{bin_file}\n{lib_file}\n"),
)
}
diff --git a/src/tools/rustfmt/src/emitter/json.rs b/src/tools/rustfmt/src/emitter/json.rs
index c7f68d467..5594196be 100644
--- a/src/tools/rustfmt/src/emitter/json.rs
+++ b/src/tools/rustfmt/src/emitter/json.rs
@@ -96,7 +96,7 @@ impl JsonEmitter {
});
}
self.mismatched_files.push(MismatchedFile {
- name: format!("{}", filename),
+ name: format!("{filename}"),
mismatches,
});
Ok(())
@@ -281,7 +281,7 @@ mod tests {
}])
.unwrap();
assert_eq!(result.has_diff, true);
- assert_eq!(&writer[..], format!("{}\n", exp_json).as_bytes());
+ assert_eq!(&writer[..], format!("{exp_json}\n").as_bytes());
}
#[test]
@@ -341,6 +341,6 @@ mod tests {
};
let exp_json = to_json_string(&vec![exp_bin, exp_lib]).unwrap();
- assert_eq!(&writer[..], format!("{}\n", exp_json).as_bytes());
+ assert_eq!(&writer[..], format!("{exp_json}\n").as_bytes());
}
}
diff --git a/src/tools/rustfmt/src/emitter/stdout.rs b/src/tools/rustfmt/src/emitter/stdout.rs
index 9fddd515e..0bbc7332d 100644
--- a/src/tools/rustfmt/src/emitter/stdout.rs
+++ b/src/tools/rustfmt/src/emitter/stdout.rs
@@ -24,9 +24,9 @@ impl Emitter for StdoutEmitter {
}: FormattedFile<'_>,
) -> Result<EmitterResult, io::Error> {
if self.verbosity != Verbosity::Quiet {
- writeln!(output, "{}:\n", filename)?;
+ writeln!(output, "{filename}:\n")?;
}
- write!(output, "{}", formatted_text)?;
+ write!(output, "{formatted_text}")?;
Ok(EmitterResult::default())
}
}
diff --git a/src/tools/rustfmt/src/expr.rs b/src/tools/rustfmt/src/expr.rs
index 03cdddc41..fa941e614 100644
--- a/src/tools/rustfmt/src/expr.rs
+++ b/src/tools/rustfmt/src/expr.rs
@@ -2,7 +2,7 @@ use std::borrow::Cow;
use std::cmp::min;
use itertools::Itertools;
-use rustc_ast::token::{Delimiter, LitKind};
+use rustc_ast::token::{Delimiter, Lit, LitKind};
use rustc_ast::{ast, ptr, token};
use rustc_span::{BytePos, Span};
@@ -26,6 +26,7 @@ use crate::rewrite::{Rewrite, RewriteContext};
use crate::shape::{Indent, Shape};
use crate::source_map::{LineRangeUtils, SpanUtils};
use crate::spanned::Spanned;
+use crate::stmt;
use crate::string::{rewrite_string, StringFormat};
use crate::types::{rewrite_path, PathContext};
use crate::utils::{
@@ -48,6 +49,10 @@ pub(crate) enum ExprType {
SubExpression,
}
+pub(crate) fn lit_ends_in_dot(lit: &Lit) -> bool {
+ matches!(lit, Lit { kind: LitKind::Float, suffix: None, symbol } if symbol.as_str().ends_with('.'))
+}
+
pub(crate) fn format_expr(
expr: &ast::Expr,
expr_type: ExprType,
@@ -127,7 +132,7 @@ pub(crate) fn format_expr(
ast::ExprKind::Tup(ref items) => {
rewrite_tuple(context, items.iter(), expr.span, shape, items.len() == 1)
}
- ast::ExprKind::Let(..) => None,
+ ast::ExprKind::Let(ref pat, ref expr, _span, _) => rewrite_let(context, shape, pat, expr),
ast::ExprKind::If(..)
| ast::ExprKind::ForLoop(..)
| ast::ExprKind::Loop(..)
@@ -182,7 +187,7 @@ pub(crate) fn format_expr(
Some(label) => format!(" {}", label.ident),
None => String::new(),
};
- Some(format!("continue{}", id_str))
+ Some(format!("continue{id_str}"))
}
ast::ExprKind::Break(ref opt_label, ref opt_expr) => {
let id_str = match *opt_label {
@@ -191,9 +196,9 @@ pub(crate) fn format_expr(
};
if let Some(ref expr) = *opt_expr {
- rewrite_unary_prefix(context, &format!("break{} ", id_str), &**expr, shape)
+ rewrite_unary_prefix(context, &format!("break{id_str} "), &**expr, shape)
} else {
- Some(format!("break{}", id_str))
+ Some(format!("break{id_str}"))
}
}
ast::ExprKind::Yield(ref opt_expr) => {
@@ -275,12 +280,7 @@ pub(crate) fn format_expr(
fn needs_space_before_range(context: &RewriteContext<'_>, lhs: &ast::Expr) -> bool {
match lhs.kind {
- ast::ExprKind::Lit(token_lit) => match token_lit.kind {
- token::LitKind::Float if token_lit.suffix.is_none() => {
- context.snippet(lhs.span).ends_with('.')
- }
- _ => false,
- },
+ ast::ExprKind::Lit(token_lit) => lit_ends_in_dot(&token_lit),
ast::ExprKind::Unary(_, ref expr) => needs_space_before_range(context, expr),
_ => false,
}
@@ -309,7 +309,7 @@ pub(crate) fn format_expr(
match (lhs.as_ref().map(|x| &**x), rhs.as_ref().map(|x| &**x)) {
(Some(lhs), Some(rhs)) => {
let sp_delim = if context.config.spaces_around_ranges() {
- format!(" {} ", delim)
+ format!(" {delim} ")
} else {
default_sp_delim(Some(lhs), Some(rhs))
};
@@ -324,7 +324,7 @@ pub(crate) fn format_expr(
}
(None, Some(rhs)) => {
let sp_delim = if context.config.spaces_around_ranges() {
- format!("{} ", delim)
+ format!("{delim} ")
} else {
default_sp_delim(None, Some(rhs))
};
@@ -332,7 +332,7 @@ pub(crate) fn format_expr(
}
(Some(lhs), None) => {
let sp_delim = if context.config.spaces_around_ranges() {
- format!(" {}", delim)
+ format!(" {delim}")
} else {
default_sp_delim(Some(lhs), None)
};
@@ -367,15 +367,15 @@ pub(crate) fn format_expr(
))
}
}
- ast::ExprKind::Async(capture_by, ref block) => {
- let mover = if capture_by == ast::CaptureBy::Value {
+ ast::ExprKind::Gen(capture_by, ref block, ref kind) => {
+ let mover = if matches!(capture_by, ast::CaptureBy::Value { .. }) {
"move "
} else {
""
};
if let rw @ Some(_) = rewrite_single_line_block(
context,
- format!("{}{}", "async ", mover).as_str(),
+ format!("{kind} {mover}").as_str(),
block,
Some(&expr.attrs),
None,
@@ -386,9 +386,7 @@ pub(crate) fn format_expr(
// 6 = `async `
let budget = shape.width.saturating_sub(6);
Some(format!(
- "{}{}{}",
- "async ",
- mover,
+ "{kind} {mover}{}",
rewrite_block(
block,
Some(&expr.attrs),
@@ -460,7 +458,7 @@ fn rewrite_empty_block(
}
if !block_contains_comment(context, block) && shape.width >= 2 {
- return Some(format!("{}{}{{}}", prefix, label_str));
+ return Some(format!("{prefix}{label_str}{{}}"));
}
// If a block contains only a single-line comment, then leave it on one line.
@@ -473,7 +471,7 @@ fn rewrite_empty_block(
&& !comment_str.starts_with("//")
&& comment_str.len() + 4 <= shape.width
{
- return Some(format!("{}{}{{ {} }}", prefix, label_str, comment_str));
+ return Some(format!("{prefix}{label_str}{{ {comment_str} }}"));
}
}
@@ -516,11 +514,11 @@ fn rewrite_single_line_block(
label: Option<ast::Label>,
shape: Shape,
) -> Option<String> {
- if is_simple_block(context, block, attrs) {
+ if let Some(block_expr) = stmt::Stmt::from_simple_block(context, block, attrs) {
let expr_shape = shape.offset_left(last_line_width(prefix))?;
- let expr_str = block.stmts[0].rewrite(context, expr_shape)?;
+ let expr_str = block_expr.rewrite(context, expr_shape)?;
let label_str = rewrite_label(label);
- let result = format!("{}{}{{ {} }}", prefix, label_str, expr_str);
+ let result = format!("{prefix}{label_str}{{ {expr_str} }}");
if result.len() <= shape.width && !result.contains('\n') {
return Some(result);
}
@@ -800,19 +798,19 @@ impl<'a> ControlFlow<'a> {
let fixed_cost = self.keyword.len() + " { } else { }".len();
if let ast::ExprKind::Block(ref else_node, _) = else_block.kind {
- if !is_simple_block(context, self.block, None)
- || !is_simple_block(context, else_node, None)
- || pat_expr_str.contains('\n')
- {
- return None;
- }
+ let (if_expr, else_expr) = match (
+ stmt::Stmt::from_simple_block(context, self.block, None),
+ stmt::Stmt::from_simple_block(context, else_node, None),
+ pat_expr_str.contains('\n'),
+ ) {
+ (Some(if_expr), Some(else_expr), false) => (if_expr, else_expr),
+ _ => return None,
+ };
let new_width = width.checked_sub(pat_expr_str.len() + fixed_cost)?;
- let expr = &self.block.stmts[0];
- let if_str = expr.rewrite(context, Shape::legacy(new_width, Indent::empty()))?;
+ let if_str = if_expr.rewrite(context, Shape::legacy(new_width, Indent::empty()))?;
let new_width = new_width.checked_sub(if_str.len())?;
- let else_expr = &else_node.stmts[0];
let else_str = else_expr.rewrite(context, Shape::legacy(new_width, Indent::empty()))?;
if if_str.contains('\n') || else_str.contains('\n') {
@@ -1100,7 +1098,7 @@ impl<'a> Rewrite for ControlFlow<'a> {
result?
};
- let mut result = format!("{}{}", cond_str, block_str);
+ let mut result = format!("{cond_str}{block_str}");
if let Some(else_block) = self.else_block {
let shape = Shape::indented(shape.indent, context.config);
@@ -1160,8 +1158,7 @@ fn rewrite_label(opt_label: Option<ast::Label>) -> Cow<'static, str> {
fn extract_comment(span: Span, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
match rewrite_missing_comment(span, shape, context) {
Some(ref comment) if !comment.is_empty() => Some(format!(
- "{indent}{}{indent}",
- comment,
+ "{indent}{comment}{indent}",
indent = shape.indent.to_string_with_newline(context.config)
)),
_ => None,
@@ -1374,7 +1371,7 @@ pub(crate) fn can_be_overflowed_expr(
}
// Handle always block-like expressions
- ast::ExprKind::Async(..) | ast::ExprKind::Block(..) | ast::ExprKind::Closure(..) => true,
+ ast::ExprKind::Gen(..) | ast::ExprKind::Block(..) | ast::ExprKind::Closure(..) => true,
// Handle `[]` and `{}`-like expressions
ast::ExprKind::Array(..) | ast::ExprKind::Struct(..) => {
@@ -1436,7 +1433,7 @@ pub(crate) fn span_ends_with_comma(context: &RewriteContext<'_>, span: Span) ->
result
}
-fn rewrite_paren(
+pub(crate) fn rewrite_paren(
context: &RewriteContext<'_>,
mut subexpr: &ast::Expr,
shape: Shape,
@@ -1452,7 +1449,7 @@ fn rewrite_paren(
let remove_nested_parens = context.config.remove_nested_parens();
loop {
// 1 = "(" or ")"
- pre_span = mk_sp(span.lo() + BytePos(1), subexpr.span.lo());
+ pre_span = mk_sp(span.lo() + BytePos(1), subexpr.span().lo());
post_span = mk_sp(subexpr.span.hi(), span.hi() - BytePos(1));
pre_comment = rewrite_missing_comment(pre_span, shape, context)?;
post_comment = rewrite_missing_comment(post_span, shape, context)?;
@@ -1474,7 +1471,7 @@ fn rewrite_paren(
let subexpr_str = subexpr.rewrite(context, sub_shape)?;
let fits_single_line = !pre_comment.contains("//") && !post_comment.contains("//");
if fits_single_line {
- Some(format!("({}{}{})", pre_comment, subexpr_str, post_comment))
+ Some(format!("({pre_comment}{subexpr_str}{post_comment})"))
} else {
rewrite_paren_in_multi_line(context, subexpr, shape, pre_span, post_span)
}
@@ -1538,7 +1535,7 @@ fn rewrite_index(
// Return if index fits in a single line.
match orig_index_rw {
Some(ref index_str) if !index_str.contains('\n') => {
- return Some(format!("{}[{}]", expr_str, index_str));
+ return Some(format!("{expr_str}[{index_str}]"));
}
_ => (),
}
@@ -1561,7 +1558,7 @@ fn rewrite_index(
indent.to_string_with_newline(context.config),
new_index_str,
)),
- (Some(ref index_str), _) => Some(format!("{}[{}]", expr_str, index_str)),
+ (Some(ref index_str), _) => Some(format!("{expr_str}[{index_str}]")),
_ => None,
}
}
@@ -1593,9 +1590,9 @@ fn rewrite_struct_lit<'a>(
let path_str = rewrite_path(context, PathContext::Expr, qself, path, path_shape)?;
let has_base_or_rest = match struct_rest {
- ast::StructRest::None if fields.is_empty() => return Some(format!("{} {{}}", path_str)),
+ ast::StructRest::None if fields.is_empty() => return Some(format!("{path_str} {{}}")),
ast::StructRest::Rest(_) if fields.is_empty() => {
- return Some(format!("{} {{ .. }}", path_str));
+ return Some(format!("{path_str} {{ .. }}"));
}
ast::StructRest::Rest(_) | ast::StructRest::Base(_) => true,
_ => false,
@@ -1686,7 +1683,7 @@ fn rewrite_struct_lit<'a>(
let fields_str =
wrap_struct_field(context, attrs, &fields_str, shape, v_shape, one_line_width)?;
- Some(format!("{} {{{}}}", path_str, fields_str))
+ Some(format!("{path_str} {{{fields_str}}}"))
// FIXME if context.config.indent_style() == Visual, but we run out
// of space, we should fall back to BlockIndent.
@@ -1716,7 +1713,7 @@ pub(crate) fn wrap_struct_field(
))
} else {
// One liner or visual indent.
- Some(format!(" {} ", fields_str))
+ Some(format!(" {fields_str} "))
}
} else {
Some(format!(
@@ -1765,7 +1762,7 @@ pub(crate) fn rewrite_field(
{
Some(attrs_str + name)
}
- Some(e) => Some(format!("{}{}{}{}", attrs_str, name, separator, e)),
+ Some(e) => Some(format!("{attrs_str}{name}{separator}{e}")),
None => {
let expr_offset = shape.indent.block_indent(context.config);
let expr = field
@@ -1830,7 +1827,41 @@ fn rewrite_tuple_in_visual_indent_style<'a, T: 'a + IntoOverflowableItem<'a>>(
.ends_with_newline(false);
let list_str = write_list(&item_vec, &fmt)?;
- Some(format!("({})", list_str))
+ Some(format!("({list_str})"))
+}
+
+fn rewrite_let(
+ context: &RewriteContext<'_>,
+ shape: Shape,
+ pat: &ast::Pat,
+ expr: &ast::Expr,
+) -> Option<String> {
+ let mut result = "let ".to_owned();
+
+ // TODO(ytmimi) comments could appear between `let` and the `pat`
+
+ // 4 = "let ".len()
+ let pat_shape = shape.offset_left(4)?;
+ let pat_str = pat.rewrite(context, pat_shape)?;
+ result.push_str(&pat_str);
+
+ // TODO(ytmimi) comments could appear between `pat` and `=`
+ result.push_str(" =");
+
+ let comments_lo = context
+ .snippet_provider
+ .span_after(expr.span.with_lo(pat.span.hi()), "=");
+ let comments_span = mk_sp(comments_lo, expr.span.lo());
+ rewrite_assign_rhs_with_comments(
+ context,
+ result,
+ expr,
+ shape,
+ &RhsAssignKind::Expr(&expr.kind, expr.span),
+ RhsTactics::Default,
+ comments_span,
+ true,
+ )
}
pub(crate) fn rewrite_tuple<'a, T: 'a + IntoOverflowableItem<'a>>(
@@ -2072,7 +2103,7 @@ fn choose_rhs<R: Rewrite>(
Some(ref new_str)
if !new_str.contains('\n') && unicode_str_width(new_str) <= shape.width =>
{
- Some(format!(" {}", new_str))
+ Some(format!(" {new_str}"))
}
_ => {
// Expression did not fit on the same line as the identifier.
@@ -2089,21 +2120,21 @@ fn choose_rhs<R: Rewrite>(
(Some(ref orig_rhs), Some(ref new_rhs))
if !filtered_str_fits(&new_rhs, context.config.max_width(), new_shape) =>
{
- Some(format!("{}{}", before_space_str, orig_rhs))
+ Some(format!("{before_space_str}{orig_rhs}"))
}
(Some(ref orig_rhs), Some(ref new_rhs))
if prefer_next_line(orig_rhs, new_rhs, rhs_tactics) =>
{
- Some(format!("{}{}", new_indent_str, new_rhs))
+ Some(format!("{new_indent_str}{new_rhs}"))
}
- (None, Some(ref new_rhs)) => Some(format!("{}{}", new_indent_str, new_rhs)),
+ (None, Some(ref new_rhs)) => Some(format!("{new_indent_str}{new_rhs}")),
(None, None) if rhs_tactics == RhsTactics::AllowOverflow => {
let shape = shape.infinite_width();
expr.rewrite(context, shape)
.map(|s| format!("{}{}", before_space_str, s))
}
(None, None) => None,
- (Some(orig_rhs), _) => Some(format!("{}{}", before_space_str, orig_rhs)),
+ (Some(orig_rhs), _) => Some(format!("{before_space_str}{orig_rhs}")),
}
}
}
diff --git a/src/tools/rustfmt/src/format-diff/main.rs b/src/tools/rustfmt/src/format-diff/main.rs
index f6b739e1c..61e2cb711 100644
--- a/src/tools/rustfmt/src/format-diff/main.rs
+++ b/src/tools/rustfmt/src/format-diff/main.rs
@@ -5,11 +5,12 @@
#![deny(warnings)]
#[macro_use]
-extern crate log;
+extern crate tracing;
use serde::{Deserialize, Serialize};
use serde_json as json;
use thiserror::Error;
+use tracing_subscriber::EnvFilter;
use std::collections::HashSet;
use std::env;
@@ -63,10 +64,12 @@ pub struct Opts {
}
fn main() {
- env_logger::Builder::from_env("RUSTFMT_LOG").init();
+ tracing_subscriber::fmt()
+ .with_env_filter(EnvFilter::from_env("RUSTFMT_LOG"))
+ .init();
let opts = Opts::parse();
if let Err(e) = run(opts) {
- println!("{}", e);
+ println!("{e}");
Opts::command()
.print_help()
.expect("cannot write to stdout");
@@ -110,7 +113,7 @@ fn run_rustfmt(files: &HashSet<String>, ranges: &[Range]) -> Result<(), FormatDi
if !exit_status.success() {
return Err(FormatDiffError::IoError(io::Error::new(
io::ErrorKind::Other,
- format!("rustfmt failed with {}", exit_status),
+ format!("rustfmt failed with {exit_status}"),
)));
}
Ok(())
@@ -126,12 +129,12 @@ fn scan_diff<R>(
where
R: io::Read,
{
- let diff_pattern = format!(r"^\+\+\+\s(?:.*?/){{{}}}(\S*)", skip_prefix);
+ let diff_pattern = format!(r"^\+\+\+\s(?:.*?/){{{skip_prefix}}}(\S*)");
let diff_pattern = Regex::new(&diff_pattern).unwrap();
let lines_pattern = Regex::new(r"^@@.*\+(\d+)(,(\d+))?").unwrap();
- let file_filter = Regex::new(&format!("^{}$", file_filter))?;
+ let file_filter = Regex::new(&format!("^{file_filter}$"))?;
let mut current_file = None;
diff --git a/src/tools/rustfmt/src/formatting.rs b/src/tools/rustfmt/src/formatting.rs
index 1f4ad6960..cd57a025b 100644
--- a/src/tools/rustfmt/src/formatting.rs
+++ b/src/tools/rustfmt/src/formatting.rs
@@ -296,7 +296,7 @@ impl<'b, T: Write + 'b> FormatHandler for Session<'b, T> {
Ok(ref result) if result.has_diff => report.add_diff(),
Err(e) => {
// Create a new error with path_str to help users see which files failed
- let err_msg = format!("{}: {}", path, e);
+ let err_msg = format!("{path}: {e}");
return Err(io::Error::new(e.kind(), err_msg).into());
}
_ => {}
diff --git a/src/tools/rustfmt/src/git-rustfmt/main.rs b/src/tools/rustfmt/src/git-rustfmt/main.rs
index 579778edb..3059d917c 100644
--- a/src/tools/rustfmt/src/git-rustfmt/main.rs
+++ b/src/tools/rustfmt/src/git-rustfmt/main.rs
@@ -1,5 +1,5 @@
#[macro_use]
-extern crate log;
+extern crate tracing;
use std::env;
use std::io::stdout;
@@ -9,6 +9,7 @@ use std::str::FromStr;
use getopts::{Matches, Options};
use rustfmt_nightly as rustfmt;
+use tracing_subscriber::EnvFilter;
use crate::rustfmt::{load_config, CliOptions, FormatReportFormatterBuilder, Input, Session};
@@ -42,7 +43,7 @@ fn git_diff(commits: &str) -> String {
let mut cmd = Command::new("git");
cmd.arg("diff");
if commits != "0" {
- cmd.arg(format!("HEAD~{}", commits));
+ cmd.arg(format!("HEAD~{commits}"));
}
let output = cmd.output().expect("Couldn't execute `git diff`");
String::from_utf8_lossy(&output.stdout).into_owned()
@@ -107,7 +108,7 @@ fn check_uncommitted() {
if !uncommitted.is_empty() {
println!("Found untracked changes:");
for f in &uncommitted {
- println!(" {}", f);
+ println!(" {f}");
}
println!("Commit your work, or run with `-u`.");
println!("Exiting.");
@@ -170,7 +171,9 @@ impl Config {
}
fn main() {
- env_logger::Builder::from_env("RUSTFMT_LOG").init();
+ tracing_subscriber::fmt()
+ .with_env_filter(EnvFilter::from_env("RUSTFMT_LOG"))
+ .init();
let opts = make_opts();
let matches = opts
diff --git a/src/tools/rustfmt/src/imports.rs b/src/tools/rustfmt/src/imports.rs
index 339e5cef5..f8e7fa628 100644
--- a/src/tools/rustfmt/src/imports.rs
+++ b/src/tools/rustfmt/src/imports.rs
@@ -191,7 +191,7 @@ impl UseSegment {
"crate" => UseSegmentKind::Crate(None),
_ => {
let mod_sep = if modsep { "::" } else { "" };
- UseSegmentKind::Ident(format!("{}{}", mod_sep, name), None)
+ UseSegmentKind::Ident(format!("{mod_sep}{name}"), None)
}
};
@@ -295,8 +295,8 @@ impl fmt::Display for UseSegmentKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
UseSegmentKind::Glob => write!(f, "*"),
- UseSegmentKind::Ident(ref s, Some(ref alias)) => write!(f, "{} as {}", s, alias),
- UseSegmentKind::Ident(ref s, None) => write!(f, "{}", s),
+ UseSegmentKind::Ident(ref s, Some(ref alias)) => write!(f, "{s} as {alias}"),
+ UseSegmentKind::Ident(ref s, None) => write!(f, "{s}"),
UseSegmentKind::Slf(..) => write!(f, "self"),
UseSegmentKind::Super(..) => write!(f, "super"),
UseSegmentKind::Crate(..) => write!(f, "crate"),
@@ -306,7 +306,7 @@ impl fmt::Display for UseSegmentKind {
if i != 0 {
write!(f, ", ")?;
}
- write!(f, "{}", item)?;
+ write!(f, "{item}")?;
}
write!(f, "}}")
}
@@ -319,7 +319,7 @@ impl fmt::Display for UseTree {
if i != 0 {
write!(f, "::")?;
}
- write!(f, "{}", segment)?;
+ write!(f, "{segment}")?;
}
Ok(())
}
@@ -589,7 +589,7 @@ impl UseTree {
// Normalise foo::{bar} -> foo::bar
if let UseSegmentKind::List(ref list) = last.kind {
- if list.len() == 1 && list[0].to_string() != "self" {
+ if list.len() == 1 && list[0].to_string() != "self" && !list[0].has_comment() {
normalize_sole_list = true;
}
}
@@ -1032,7 +1032,9 @@ fn rewrite_nested_use_tree(
let list_str = write_list(&list_items, &fmt)?;
- let result = if (list_str.contains('\n') || list_str.len() > remaining_width)
+ let result = if (list_str.contains('\n')
+ || list_str.len() > remaining_width
+ || tactic == DefinitiveListTactic::Vertical)
&& context.config.imports_indent() == IndentStyle::Block
{
format!(
@@ -1042,7 +1044,7 @@ fn rewrite_nested_use_tree(
shape.indent.to_string(context.config)
)
} else {
- format!("{{{}}}", list_str)
+ format!("{{{list_str}}}")
};
Some(result)
@@ -1052,14 +1054,14 @@ impl Rewrite for UseSegment {
fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
Some(match self.kind {
UseSegmentKind::Ident(ref ident, Some(ref rename)) => {
- format!("{} as {}", ident, rename)
+ format!("{ident} as {rename}")
}
UseSegmentKind::Ident(ref ident, None) => ident.clone(),
- UseSegmentKind::Slf(Some(ref rename)) => format!("self as {}", rename),
+ UseSegmentKind::Slf(Some(ref rename)) => format!("self as {rename}"),
UseSegmentKind::Slf(None) => "self".to_owned(),
- UseSegmentKind::Super(Some(ref rename)) => format!("super as {}", rename),
+ UseSegmentKind::Super(Some(ref rename)) => format!("super as {rename}"),
UseSegmentKind::Super(None) => "super".to_owned(),
- UseSegmentKind::Crate(Some(ref rename)) => format!("crate as {}", rename),
+ UseSegmentKind::Crate(Some(ref rename)) => format!("crate as {rename}"),
UseSegmentKind::Crate(None) => "crate".to_owned(),
UseSegmentKind::Glob => "*".to_owned(),
UseSegmentKind::List(ref use_tree_list) => rewrite_nested_use_tree(
diff --git a/src/tools/rustfmt/src/items.rs b/src/tools/rustfmt/src/items.rs
index d5bc38303..edb5a5b62 100644
--- a/src/tools/rustfmt/src/items.rs
+++ b/src/tools/rustfmt/src/items.rs
@@ -75,6 +75,7 @@ impl Rewrite for ast::Local {
false,
)?
};
+ let let_kw_offset = result.len() - "let ".len();
// 4 = "let ".len()
let pat_shape = shape.offset_left(4)?;
@@ -127,8 +128,15 @@ impl Rewrite for ast::Local {
if let Some(block) = else_block {
let else_kw_span = init.span.between(block.span);
+ // Strip attributes and comments to check if newline is needed before the else
+ // keyword from the initializer part. (#5901)
+ let init_str = if context.config.version() == Version::Two {
+ &result[let_kw_offset..]
+ } else {
+ result.as_str()
+ };
let force_newline_else = pat_str.contains('\n')
- || !same_line_else_kw_and_brace(&result, context, else_kw_span, nested_shape);
+ || !same_line_else_kw_and_brace(init_str, context, else_kw_span, nested_shape);
let else_kw = rewrite_else_kw_with_comments(
force_newline_else,
true,
@@ -146,11 +154,16 @@ impl Rewrite for ast::Local {
std::cmp::min(shape.width, context.config.single_line_let_else_max_width());
// If available_space hits zero we know for sure this will be a multi-lined block
- let available_space = max_width.saturating_sub(result.len());
+ let assign_str_with_else_kw = if context.config.version() == Version::Two {
+ &result[let_kw_offset..]
+ } else {
+ result.as_str()
+ };
+ let available_space = max_width.saturating_sub(assign_str_with_else_kw.len());
let allow_single_line = !force_newline_else
&& available_space > 0
- && allow_single_line_let_else_block(&result, block);
+ && allow_single_line_let_else_block(assign_str_with_else_kw, block);
let mut rw_else_block =
rewrite_let_else_block(block, allow_single_line, context, shape)?;
@@ -248,7 +261,6 @@ impl<'a> Item<'a> {
abi: format_extern(
ast::Extern::from_abi(fm.abi, DUMMY_SP),
config.force_explicit_abi(),
- true,
),
vis: None,
body: fm
@@ -306,22 +318,20 @@ impl<'a> FnSig<'a> {
defaultness: ast::Defaultness,
) -> FnSig<'a> {
match *fn_kind {
- visit::FnKind::Fn(fn_ctxt, _, fn_sig, vis, generics, _) => match fn_ctxt {
- visit::FnCtxt::Assoc(..) => {
- let mut fn_sig = FnSig::from_method_sig(fn_sig, generics, vis);
- fn_sig.defaultness = defaultness;
- fn_sig
- }
- _ => FnSig {
- decl,
- generics,
- ext: fn_sig.header.ext,
- constness: fn_sig.header.constness,
- is_async: Cow::Borrowed(&fn_sig.header.asyncness),
- defaultness,
- unsafety: fn_sig.header.unsafety,
- visibility: vis,
- },
+ visit::FnKind::Fn(visit::FnCtxt::Assoc(..), _, fn_sig, vis, generics, _) => {
+ let mut fn_sig = FnSig::from_method_sig(fn_sig, generics, vis);
+ fn_sig.defaultness = defaultness;
+ fn_sig
+ }
+ visit::FnKind::Fn(_, _, fn_sig, vis, generics, _) => FnSig {
+ decl,
+ generics,
+ ext: fn_sig.header.ext,
+ constness: fn_sig.header.constness,
+ is_async: Cow::Borrowed(&fn_sig.header.asyncness),
+ defaultness,
+ unsafety: fn_sig.header.unsafety,
+ visibility: vis,
},
_ => unreachable!(),
}
@@ -338,7 +348,6 @@ impl<'a> FnSig<'a> {
result.push_str(&format_extern(
self.ext,
context.config.force_explicit_abi(),
- false,
));
result
}
@@ -472,7 +481,7 @@ impl<'a> FmtVisitor<'a> {
&& self.block_indent.width() + fn_str.len() + 3 <= self.config.max_width()
&& !last_line_contains_single_line_comment(fn_str)
{
- return Some(format!("{} {{}}", fn_str));
+ return Some(format!("{fn_str} {{}}"));
}
if !self.config.fn_single_line() || !is_simple_block_stmt(&context, block, None) {
@@ -484,7 +493,7 @@ impl<'a> FmtVisitor<'a> {
let width = self.block_indent.width() + fn_str.len() + res.len() + 5;
if !res.contains('\n') && width <= self.config.max_width() {
- Some(format!("{} {{ {} }}", fn_str, res))
+ Some(format!("{fn_str} {{ {res} }}"))
} else {
None
}
@@ -666,7 +675,7 @@ impl<'a> FmtVisitor<'a> {
};
let variant_body = if let Some(ref expr) = field.disr_expr {
- let lhs = format!("{:1$} =", variant_body, pad_discrim_ident_to);
+ let lhs = format!("{variant_body:pad_discrim_ident_to$} =");
let ex = &*expr.value;
rewrite_assign_rhs_with(
&context,
@@ -829,7 +838,7 @@ pub(crate) fn format_impl(
if generics.where_clause.predicates.len() == 1 {
result.push(',');
}
- result.push_str(&format!("{}{{{}}}", sep, sep));
+ result.push_str(&format!("{sep}{{{sep}}}"));
} else {
result.push_str(" {}");
}
@@ -1020,7 +1029,7 @@ fn rewrite_trait_ref(
let shape = Shape::indented(offset + used_space, context.config);
if let Some(trait_ref_str) = trait_ref.rewrite(context, shape) {
if !trait_ref_str.contains('\n') {
- return Some(format!(" {}{}", polarity_str, trait_ref_str));
+ return Some(format!(" {polarity_str}{trait_ref_str}"));
}
}
// We could not make enough space for trait_ref, so put it on new line.
@@ -1118,172 +1127,172 @@ pub(crate) fn format_trait(
item: &ast::Item,
offset: Indent,
) -> Option<String> {
- if let ast::ItemKind::Trait(trait_kind) = &item.kind {
- let ast::Trait {
- is_auto,
- unsafety,
- ref generics,
- ref bounds,
- ref items,
- } = **trait_kind;
- let mut result = String::with_capacity(128);
- let header = format!(
- "{}{}{}trait ",
- format_visibility(context, &item.vis),
- format_unsafety(unsafety),
- format_auto(is_auto),
- );
- result.push_str(&header);
+ let ast::ItemKind::Trait(trait_kind) = &item.kind else {
+ unreachable!();
+ };
+ let ast::Trait {
+ is_auto,
+ unsafety,
+ ref generics,
+ ref bounds,
+ ref items,
+ } = **trait_kind;
- let body_lo = context.snippet_provider.span_after(item.span, "{");
+ let mut result = String::with_capacity(128);
+ let header = format!(
+ "{}{}{}trait ",
+ format_visibility(context, &item.vis),
+ format_unsafety(unsafety),
+ format_auto(is_auto),
+ );
+ result.push_str(&header);
- let shape = Shape::indented(offset, context.config).offset_left(result.len())?;
- let generics_str =
- rewrite_generics(context, rewrite_ident(context, item.ident), generics, shape)?;
- result.push_str(&generics_str);
+ let body_lo = context.snippet_provider.span_after(item.span, "{");
- // FIXME(#2055): rustfmt fails to format when there are comments between trait bounds.
- if !bounds.is_empty() {
- let ident_hi = context
- .snippet_provider
- .span_after(item.span, item.ident.as_str());
- let bound_hi = bounds.last().unwrap().span().hi();
- let snippet = context.snippet(mk_sp(ident_hi, bound_hi));
- if contains_comment(snippet) {
- return None;
- }
+ let shape = Shape::indented(offset, context.config).offset_left(result.len())?;
+ let generics_str =
+ rewrite_generics(context, rewrite_ident(context, item.ident), generics, shape)?;
+ result.push_str(&generics_str);
- result = rewrite_assign_rhs_with(
- context,
- result + ":",
- bounds,
- shape,
- &RhsAssignKind::Bounds,
- RhsTactics::ForceNextLineWithoutIndent,
- )?;
+ // FIXME(#2055): rustfmt fails to format when there are comments between trait bounds.
+ if !bounds.is_empty() {
+ let ident_hi = context
+ .snippet_provider
+ .span_after(item.span, item.ident.as_str());
+ let bound_hi = bounds.last().unwrap().span().hi();
+ let snippet = context.snippet(mk_sp(ident_hi, bound_hi));
+ if contains_comment(snippet) {
+ return None;
}
- // Rewrite where-clause.
- if !generics.where_clause.predicates.is_empty() {
- let where_on_new_line = context.config.indent_style() != IndentStyle::Block;
+ result = rewrite_assign_rhs_with(
+ context,
+ result + ":",
+ bounds,
+ shape,
+ &RhsAssignKind::Bounds,
+ RhsTactics::ForceNextLineWithoutIndent,
+ )?;
+ }
+
+ // Rewrite where-clause.
+ if !generics.where_clause.predicates.is_empty() {
+ let where_on_new_line = context.config.indent_style() != IndentStyle::Block;
- let where_budget = context.budget(last_line_width(&result));
- let pos_before_where = if bounds.is_empty() {
- generics.where_clause.span.lo()
+ let where_budget = context.budget(last_line_width(&result));
+ let pos_before_where = if bounds.is_empty() {
+ generics.where_clause.span.lo()
+ } else {
+ bounds[bounds.len() - 1].span().hi()
+ };
+ let option = WhereClauseOption::snuggled(&generics_str);
+ let where_clause_str = rewrite_where_clause(
+ context,
+ &generics.where_clause.predicates,
+ generics.where_clause.span,
+ context.config.brace_style(),
+ Shape::legacy(where_budget, offset.block_only()),
+ where_on_new_line,
+ "{",
+ None,
+ pos_before_where,
+ option,
+ )?;
+ // If the where-clause cannot fit on the same line,
+ // put the where-clause on a new line
+ if !where_clause_str.contains('\n')
+ && last_line_width(&result) + where_clause_str.len() + offset.width()
+ > context.config.comment_width()
+ {
+ let width = offset.block_indent + context.config.tab_spaces() - 1;
+ let where_indent = Indent::new(0, width);
+ result.push_str(&where_indent.to_string_with_newline(context.config));
+ }
+ result.push_str(&where_clause_str);
+ } else {
+ let item_snippet = context.snippet(item.span);
+ if let Some(lo) = item_snippet.find('/') {
+ // 1 = `{`
+ let comment_hi = if generics.params.len() > 0 {
+ generics.span.lo() - BytePos(1)
} else {
- bounds[bounds.len() - 1].span().hi()
+ body_lo - BytePos(1)
};
- let option = WhereClauseOption::snuggled(&generics_str);
- let where_clause_str = rewrite_where_clause(
- context,
- &generics.where_clause.predicates,
- generics.where_clause.span,
- context.config.brace_style(),
- Shape::legacy(where_budget, offset.block_only()),
- where_on_new_line,
- "{",
- None,
- pos_before_where,
- option,
- )?;
- // If the where-clause cannot fit on the same line,
- // put the where-clause on a new line
- if !where_clause_str.contains('\n')
- && last_line_width(&result) + where_clause_str.len() + offset.width()
- > context.config.comment_width()
- {
- let width = offset.block_indent + context.config.tab_spaces() - 1;
- let where_indent = Indent::new(0, width);
- result.push_str(&where_indent.to_string_with_newline(context.config));
- }
- result.push_str(&where_clause_str);
- } else {
- let item_snippet = context.snippet(item.span);
- if let Some(lo) = item_snippet.find('/') {
- // 1 = `{`
- let comment_hi = if generics.params.len() > 0 {
- generics.span.lo() - BytePos(1)
- } else {
- body_lo - BytePos(1)
- };
- let comment_lo = item.span.lo() + BytePos(lo as u32);
- if comment_lo < comment_hi {
- match recover_missing_comment_in_span(
- mk_sp(comment_lo, comment_hi),
- Shape::indented(offset, context.config),
- context,
- last_line_width(&result),
- ) {
- Some(ref missing_comment) if !missing_comment.is_empty() => {
- result.push_str(missing_comment);
- }
- _ => (),
+ let comment_lo = item.span.lo() + BytePos(lo as u32);
+ if comment_lo < comment_hi {
+ match recover_missing_comment_in_span(
+ mk_sp(comment_lo, comment_hi),
+ Shape::indented(offset, context.config),
+ context,
+ last_line_width(&result),
+ ) {
+ Some(ref missing_comment) if !missing_comment.is_empty() => {
+ result.push_str(missing_comment);
}
+ _ => (),
}
}
}
+ }
- let block_span = mk_sp(generics.where_clause.span.hi(), item.span.hi());
- let snippet = context.snippet(block_span);
- let open_pos = snippet.find_uncommented("{")? + 1;
+ let block_span = mk_sp(generics.where_clause.span.hi(), item.span.hi());
+ let snippet = context.snippet(block_span);
+ let open_pos = snippet.find_uncommented("{")? + 1;
- match context.config.brace_style() {
- _ if last_line_contains_single_line_comment(&result)
- || last_line_width(&result) + 2 > context.budget(offset.width()) =>
- {
- result.push_str(&offset.to_string_with_newline(context.config));
- }
- _ if context.config.empty_item_single_line()
- && items.is_empty()
- && !result.contains('\n')
- && !contains_comment(&snippet[open_pos..]) =>
+ match context.config.brace_style() {
+ _ if last_line_contains_single_line_comment(&result)
+ || last_line_width(&result) + 2 > context.budget(offset.width()) =>
+ {
+ result.push_str(&offset.to_string_with_newline(context.config));
+ }
+ _ if context.config.empty_item_single_line()
+ && items.is_empty()
+ && !result.contains('\n')
+ && !contains_comment(&snippet[open_pos..]) =>
+ {
+ result.push_str(" {}");
+ return Some(result);
+ }
+ BraceStyle::AlwaysNextLine => {
+ result.push_str(&offset.to_string_with_newline(context.config));
+ }
+ BraceStyle::PreferSameLine => result.push(' '),
+ BraceStyle::SameLineWhere => {
+ if result.contains('\n')
+ || (!generics.where_clause.predicates.is_empty() && !items.is_empty())
{
- result.push_str(" {}");
- return Some(result);
- }
- BraceStyle::AlwaysNextLine => {
result.push_str(&offset.to_string_with_newline(context.config));
- }
- BraceStyle::PreferSameLine => result.push(' '),
- BraceStyle::SameLineWhere => {
- if result.contains('\n')
- || (!generics.where_clause.predicates.is_empty() && !items.is_empty())
- {
- result.push_str(&offset.to_string_with_newline(context.config));
- } else {
- result.push(' ');
- }
+ } else {
+ result.push(' ');
}
}
- result.push('{');
-
- let outer_indent_str = offset.block_only().to_string_with_newline(context.config);
+ }
+ result.push('{');
- if !items.is_empty() || contains_comment(&snippet[open_pos..]) {
- let mut visitor = FmtVisitor::from_context(context);
- visitor.block_indent = offset.block_only().block_indent(context.config);
- visitor.last_pos = block_span.lo() + BytePos(open_pos as u32);
+ let outer_indent_str = offset.block_only().to_string_with_newline(context.config);
- for item in items {
- visitor.visit_trait_item(item);
- }
+ if !items.is_empty() || contains_comment(&snippet[open_pos..]) {
+ let mut visitor = FmtVisitor::from_context(context);
+ visitor.block_indent = offset.block_only().block_indent(context.config);
+ visitor.last_pos = block_span.lo() + BytePos(open_pos as u32);
- visitor.format_missing(item.span.hi() - BytePos(1));
+ for item in items {
+ visitor.visit_trait_item(item);
+ }
- let inner_indent_str = visitor.block_indent.to_string_with_newline(context.config);
+ visitor.format_missing(item.span.hi() - BytePos(1));
- result.push_str(&inner_indent_str);
- result.push_str(visitor.buffer.trim());
- result.push_str(&outer_indent_str);
- } else if result.contains('\n') {
- result.push_str(&outer_indent_str);
- }
+ let inner_indent_str = visitor.block_indent.to_string_with_newline(context.config);
- result.push('}');
- Some(result)
- } else {
- unreachable!();
+ result.push_str(&inner_indent_str);
+ result.push_str(visitor.buffer.trim());
+ result.push_str(&outer_indent_str);
+ } else if result.contains('\n') {
+ result.push_str(&outer_indent_str);
}
+
+ result.push('}');
+ Some(result)
}
pub(crate) struct TraitAliasBounds<'a> {
@@ -1322,7 +1331,7 @@ impl<'a> Rewrite for TraitAliasBounds<'a> {
shape.indent.to_string_with_newline(context.config)
};
- Some(format!("{}{}{}", generic_bounds_str, space, where_str))
+ Some(format!("{generic_bounds_str}{space}{where_str}"))
}
}
@@ -1339,7 +1348,7 @@ pub(crate) fn format_trait_alias(
let g_shape = shape.offset_left(6)?.sub_width(2)?;
let generics_str = rewrite_generics(context, alias, generics, g_shape)?;
let vis_str = format_visibility(context, vis);
- let lhs = format!("{}trait {} =", vis_str, generics_str);
+ let lhs = format!("{vis_str}trait {generics_str} =");
// 1 = ";"
let trait_alias_bounds = TraitAliasBounds {
generic_bounds,
@@ -1376,7 +1385,7 @@ fn format_unit_struct(
} else {
String::new()
};
- Some(format!("{}{};", header_str, generics_str))
+ Some(format!("{header_str}{generics_str};"))
}
pub(crate) fn format_struct_struct(
@@ -1466,7 +1475,7 @@ pub(crate) fn format_struct_struct(
&& items_str.len() <= one_line_budget
&& !last_line_contains_single_line_comment(&items_str)
{
- Some(format!("{} {} }}", result, items_str))
+ Some(format!("{result} {items_str} }}"))
} else {
Some(format!(
"{}\n{}{}\n{}}}",
@@ -1696,7 +1705,7 @@ pub(crate) fn rewrite_type_alias<'a, 'b>(
rewrite_ty(rw_info, Some(bounds), ty_opt, vis)
}?;
match defaultness {
- ast::Defaultness::Default(..) => Some(format!("default {}", result)),
+ ast::Defaultness::Default(..) => Some(format!("default {result}")),
_ => Some(result),
}
}
@@ -1803,14 +1812,14 @@ fn rewrite_ty<R: Rewrite>(
true,
)?
}
- _ => format!("{}=", result),
+ _ => format!("{result}="),
};
// 1 = `;`
let shape = Shape::indented(indent, context.config).sub_width(1)?;
rewrite_assign_rhs(context, lhs, &*ty, &RhsAssignKind::Ty, shape).map(|s| s + ";")
} else {
- Some(format!("{};", result))
+ Some(format!("{result};"))
}
}
@@ -2019,7 +2028,7 @@ fn rewrite_static(
let expr_lo = expr.span.lo();
let comments_span = mk_sp(comments_lo, expr_lo);
- let lhs = format!("{}{} =", prefix, ty_str);
+ let lhs = format!("{prefix}{ty_str} =");
// 1 = ;
let remaining_width = context.budget(offset.block_indent + 1);
@@ -2036,7 +2045,7 @@ fn rewrite_static(
.and_then(|res| recover_comment_removed(res, static_parts.span, context))
.map(|s| if s.ends_with(';') { s } else { s + ";" })
} else {
- Some(format!("{}{};", prefix, ty_str))
+ Some(format!("{prefix}{ty_str};"))
}
}
@@ -2229,7 +2238,7 @@ fn rewrite_explicit_self(
Some(combine_strs_with_missing_comments(
context,
param_attrs,
- &format!("&{} {}self", lifetime_str, mut_str),
+ &format!("&{lifetime_str} {mut_str}self"),
span,
shape,
!has_multiple_attr_lines,
@@ -2238,7 +2247,7 @@ fn rewrite_explicit_self(
None => Some(combine_strs_with_missing_comments(
context,
param_attrs,
- &format!("&{}self", mut_str),
+ &format!("&{mut_str}self"),
span,
shape,
!has_multiple_attr_lines,
@@ -2599,7 +2608,8 @@ fn rewrite_fn_base(
if where_clause_str.is_empty() {
if let ast::FnRetTy::Default(ret_span) = fd.output {
match recover_missing_comment_in_span(
- mk_sp(params_span.hi(), ret_span.hi()),
+ // from after the closing paren to right before block or semicolon
+ mk_sp(ret_span.lo(), span.hi()),
shape,
context,
last_line_width(&result),
@@ -2908,7 +2918,7 @@ fn rewrite_where_clause_rfc_style(
clause_shape.indent.to_string_with_newline(context.config)
};
- Some(format!("{}{}{}", where_keyword, clause_sep, preds_str))
+ Some(format!("{where_keyword}{clause_sep}{preds_str}"))
}
/// Rewrite `where` and comment around it.
@@ -2948,8 +2958,8 @@ fn rewrite_where_keyword(
let newline_before_where = comment_separator(&comment_before, shape);
let newline_after_where = comment_separator(&comment_after, clause_shape);
let result = format!(
- "{}{}{}where{}{}",
- starting_newline, comment_before, newline_before_where, newline_after_where, comment_after
+ "{starting_newline}{comment_before}{newline_before_where}where\
+{newline_after_where}{comment_after}"
);
let allow_single_line = where_clause_option.allow_single_line
&& comment_before.is_empty()
@@ -3000,10 +3010,12 @@ fn rewrite_bounds_on_where_clause(
DefinitiveListTactic::Vertical
};
+ let preserve_newline = context.config.version() == Version::One;
+
let fmt = ListFormatting::new(shape, context.config)
.tactic(shape_tactic)
.trailing_separator(comma_tactic)
- .preserve_newline(true);
+ .preserve_newline(preserve_newline);
write_list(&items.collect::<Vec<_>>(), &fmt)
}
@@ -3104,7 +3116,7 @@ fn rewrite_where_clause(
preds_str
))
} else {
- Some(format!(" where {}", preds_str))
+ Some(format!(" where {preds_str}"))
}
}
@@ -3233,7 +3245,7 @@ fn format_generics(
if brace_pos == BracePos::None {
span.hi()
} else {
- context.snippet_provider.span_before(span, "{")
+ context.snippet_provider.span_before_last(span, "{")
},
),
shape,
diff --git a/src/tools/rustfmt/src/lib.rs b/src/tools/rustfmt/src/lib.rs
index 567628f63..877d057a3 100644
--- a/src/tools/rustfmt/src/lib.rs
+++ b/src/tools/rustfmt/src/lib.rs
@@ -9,7 +9,7 @@
#[macro_use]
extern crate lazy_static;
#[macro_use]
-extern crate log;
+extern crate tracing;
// N.B. these crates are loaded from the sysroot, so they need extern crate.
extern crate rustc_ast;
@@ -29,6 +29,7 @@ extern crate thin_vec;
extern crate rustc_driver;
use std::cell::RefCell;
+use std::cmp::min;
use std::collections::HashMap;
use std::fmt;
use std::io::{self, Write};
@@ -386,9 +387,15 @@ fn format_code_block(
.snippet
.rfind('}')
.unwrap_or_else(|| formatted.snippet.len());
+
+ // It's possible that `block_len < FN_MAIN_PREFIX.len()`. This can happen if the code block was
+ // formatted into the empty string, leading to the enclosing `fn main() {\n}` being formatted
+ // into `fn main() {}`. In this case no unindentation is done.
+ let block_start = min(FN_MAIN_PREFIX.len(), block_len);
+
let mut is_indented = true;
let indent_str = Indent::from_width(config, config.tab_spaces()).to_string(config);
- for (kind, ref line) in LineClasses::new(&formatted.snippet[FN_MAIN_PREFIX.len()..block_len]) {
+ for (kind, ref line) in LineClasses::new(&formatted.snippet[block_start..block_len]) {
if !is_first {
result.push('\n');
} else {
diff --git a/src/tools/rustfmt/src/lists.rs b/src/tools/rustfmt/src/lists.rs
index a878e6cf9..41afef279 100644
--- a/src/tools/rustfmt/src/lists.rs
+++ b/src/tools/rustfmt/src/lists.rs
@@ -637,7 +637,7 @@ pub(crate) fn extract_post_comment(
post_snippet.trim_matches(white_space)
}
// not comment or over two lines
- else if post_snippet.ends_with(',')
+ else if post_snippet.ends_with(separator)
&& (!post_snippet.trim().starts_with("//") || post_snippet.trim().contains('\n'))
{
post_snippet[..(post_snippet.len() - 1)].trim_matches(white_space)
diff --git a/src/tools/rustfmt/src/macros.rs b/src/tools/rustfmt/src/macros.rs
index 4f45d0c74..76553466e 100644
--- a/src/tools/rustfmt/src/macros.rs
+++ b/src/tools/rustfmt/src/macros.rs
@@ -103,7 +103,7 @@ fn rewrite_macro_name(
format!("{}!", pprust::path_to_string(path))
};
match extra_ident {
- Some(ident) if ident.name != kw::Empty => format!("{} {}", name, ident),
+ Some(ident) if ident.name != kw::Empty => format!("{name} {ident}"),
_ => name,
}
}
@@ -214,14 +214,14 @@ fn rewrite_macro_inner(
if ts.is_empty() && !has_comment {
return match style {
Delimiter::Parenthesis if position == MacroPosition::Item => {
- Some(format!("{}();", macro_name))
+ Some(format!("{macro_name}();"))
}
Delimiter::Bracket if position == MacroPosition::Item => {
- Some(format!("{}[];", macro_name))
+ Some(format!("{macro_name}[];"))
}
- Delimiter::Parenthesis => Some(format!("{}()", macro_name)),
- Delimiter::Bracket => Some(format!("{}[]", macro_name)),
- Delimiter::Brace => Some(format!("{} {{}}", macro_name)),
+ Delimiter::Parenthesis => Some(format!("{macro_name}()")),
+ Delimiter::Bracket => Some(format!("{macro_name}[]")),
+ Delimiter::Brace => Some(format!("{macro_name} {{}}")),
_ => unreachable!(),
};
}
@@ -255,6 +255,7 @@ fn rewrite_macro_inner(
&macro_name,
shape,
style,
+ original_style,
position,
mac.span(),
);
@@ -295,20 +296,19 @@ fn rewrite_macro_inner(
// If we are rewriting `vec!` macro or other special macros,
// then we can rewrite this as a usual array literal.
// Otherwise, we must preserve the original existence of trailing comma.
- let macro_name = &macro_name.as_str();
let mut force_trailing_comma = if trailing_comma {
Some(SeparatorTactic::Always)
} else {
Some(SeparatorTactic::Never)
};
- if FORCED_BRACKET_MACROS.contains(macro_name) && !is_nested_macro {
+ if is_forced_bracket && !is_nested_macro {
context.leave_macro();
if context.use_block_indent() {
force_trailing_comma = Some(SeparatorTactic::Vertical);
};
}
let rewrite = rewrite_array(
- macro_name,
+ &macro_name,
arg_vec.iter(),
mac.span(),
context,
@@ -321,7 +321,7 @@ fn rewrite_macro_inner(
_ => "",
};
- Some(format!("{}{}", rewrite, comma))
+ Some(format!("{rewrite}{comma}"))
}
}
Delimiter::Brace => {
@@ -330,8 +330,8 @@ fn rewrite_macro_inner(
// anything in between the braces (for now).
let snippet = context.snippet(mac.span()).trim_start_matches(|c| c != '{');
match trim_left_preserve_layout(snippet, shape.indent, context.config) {
- Some(macro_body) => Some(format!("{} {}", macro_name, macro_body)),
- None => Some(format!("{} {}", macro_name, snippet)),
+ Some(macro_body) => Some(format!("{macro_name} {macro_body}")),
+ None => Some(format!("{macro_name} {snippet}")),
}
}
_ => unreachable!(),
@@ -362,7 +362,7 @@ fn handle_vec_semi(
&& lhs.len() + rhs.len() + total_overhead <= shape.width
{
// macro_name(lhs; rhs) or macro_name[lhs; rhs]
- Some(format!("{}{}{}; {}{}", macro_name, left, lhs, rhs, right))
+ Some(format!("{macro_name}{left}{lhs}; {rhs}{right}"))
} else {
// macro_name(\nlhs;\nrhs\n) or macro_name[\nlhs;\nrhs\n]
Some(format!(
@@ -379,6 +379,23 @@ fn handle_vec_semi(
}
}
+fn rewrite_empty_macro_def_body(
+ context: &RewriteContext<'_>,
+ span: Span,
+ shape: Shape,
+) -> Option<String> {
+ // Create an empty, dummy `ast::Block` representing an empty macro body
+ let block = ast::Block {
+ stmts: vec![].into(),
+ id: rustc_ast::node_id::DUMMY_NODE_ID,
+ rules: ast::BlockCheckMode::Default,
+ span: span,
+ tokens: None,
+ could_be_bare_literal: false,
+ };
+ block.rewrite(context, shape)
+}
+
pub(crate) fn rewrite_macro_def(
context: &RewriteContext<'_>,
shape: Shape,
@@ -419,6 +436,13 @@ pub(crate) fn rewrite_macro_def(
shape
};
+ if parsed_def.branches.len() == 0 {
+ let lo = context.snippet_provider.span_before(span, "{");
+ result += " ";
+ result += &rewrite_empty_macro_def_body(context, span.with_lo(lo), shape)?;
+ return Some(result);
+ }
+
let branch_items = itemize_list(
context.snippet_provider,
parsed_def.branches.iter(),
@@ -572,8 +596,8 @@ fn delim_token_to_str(
.block_indent(context.config)
.to_string_with_newline(context.config);
(
- format!("{}{}", lhs, nested_indent_str),
- format!("{}{}", indent_str, rhs),
+ format!("{lhs}{nested_indent_str}"),
+ format!("{indent_str}{rhs}"),
)
} else {
(lhs.to_owned(), rhs.to_owned())
@@ -630,7 +654,7 @@ impl MacroArgKind {
};
match *self {
- MacroArgKind::MetaVariable(ty, ref name) => Some(format!("${}:{}", name, ty)),
+ MacroArgKind::MetaVariable(ty, ref name) => Some(format!("${name}:{ty}")),
MacroArgKind::Repeat(delim_tok, ref args, ref another, ref tok) => {
let (lhs, inner, rhs) = rewrite_delimited_inner(delim_tok, args)?;
let another = another
@@ -639,14 +663,14 @@ impl MacroArgKind {
.unwrap_or_else(|| "".to_owned());
let repeat_tok = pprust::token_to_string(tok);
- Some(format!("${}{}{}{}{}", lhs, inner, rhs, another, repeat_tok))
+ Some(format!("${lhs}{inner}{rhs}{another}{repeat_tok}"))
}
MacroArgKind::Delimited(delim_tok, ref args) => {
rewrite_delimited_inner(delim_tok, args)
.map(|(lhs, inner, rhs)| format!("{}{}{}", lhs, inner, rhs))
}
- MacroArgKind::Separator(ref sep, ref prefix) => Some(format!("{}{} ", prefix, sep)),
- MacroArgKind::Other(ref inner, ref prefix) => Some(format!("{}{}", prefix, inner)),
+ MacroArgKind::Separator(ref sep, ref prefix) => Some(format!("{prefix}{sep} ")),
+ MacroArgKind::Other(ref inner, ref prefix) => Some(format!("{prefix}{inner}")),
}
}
}
@@ -1378,15 +1402,19 @@ fn rewrite_macro_with_items(
macro_name: &str,
shape: Shape,
style: Delimiter,
+ original_style: Delimiter,
position: MacroPosition,
span: Span,
) -> Option<String> {
- let (opener, closer) = match style {
- Delimiter::Parenthesis => ("(", ")"),
- Delimiter::Bracket => ("[", "]"),
- Delimiter::Brace => (" {", "}"),
- _ => return None,
+ let style_to_delims = |style| match style {
+ Delimiter::Parenthesis => Some(("(", ")")),
+ Delimiter::Bracket => Some(("[", "]")),
+ Delimiter::Brace => Some((" {", "}")),
+ _ => None,
};
+
+ let (opener, closer) = style_to_delims(style)?;
+ let (original_opener, _) = style_to_delims(original_style)?;
let trailing_semicolon = match style {
Delimiter::Parenthesis | Delimiter::Bracket if position == MacroPosition::Item => ";",
_ => "",
@@ -1394,7 +1422,13 @@ fn rewrite_macro_with_items(
let mut visitor = FmtVisitor::from_context(context);
visitor.block_indent = shape.indent.block_indent(context.config);
- visitor.last_pos = context.snippet_provider.span_after(span, opener.trim());
+
+ // The current opener may be different from the original opener. This can happen
+ // if our macro is a forced bracket macro originally written with non-bracket
+ // delimiters. We need to use the original opener to locate the span after it.
+ visitor.last_pos = context
+ .snippet_provider
+ .span_after(span, original_opener.trim());
for item in items {
let item = match item {
MacroArg::Item(item) => item,
diff --git a/src/tools/rustfmt/src/matches.rs b/src/tools/rustfmt/src/matches.rs
index 4c37116f1..95b0ed16d 100644
--- a/src/tools/rustfmt/src/matches.rs
+++ b/src/tools/rustfmt/src/matches.rs
@@ -124,7 +124,7 @@ pub(crate) fn rewrite_match(
if arms.is_empty() {
let snippet = context.snippet(mk_sp(open_brace_pos, span.hi() - BytePos(1)));
if snippet.trim().is_empty() {
- Some(format!("match {} {{}}", cond_str))
+ Some(format!("match {cond_str} {{}}"))
} else {
// Empty match with comments or inner attributes? We are not going to bother, sorry ;)
Some(context.snippet(span).to_owned())
@@ -246,8 +246,18 @@ fn rewrite_match_arm(
};
// Patterns
- // 5 = ` => {`
- let pat_shape = shape.sub_width(5)?.offset_left(pipe_offset)?;
+ let pat_shape = match &arm.body.kind {
+ ast::ExprKind::Block(_, Some(label)) => {
+ // Some block with a label ` => 'label: {`
+ // 7 = ` => : {`
+ let label_len = label.ident.as_str().len();
+ shape.sub_width(7 + label_len)?.offset_left(pipe_offset)?
+ }
+ _ => {
+ // 5 = ` => {`
+ shape.sub_width(5)?.offset_left(pipe_offset)?
+ }
+ };
let pats_str = arm.pat.rewrite(context, pat_shape)?;
// Guard
@@ -264,7 +274,7 @@ fn rewrite_match_arm(
let lhs_str = combine_strs_with_missing_comments(
context,
&attrs_str,
- &format!("{}{}{}", pipe_str, pats_str, guard_str),
+ &format!("{pipe_str}{pats_str}{guard_str}"),
missing_span,
shape,
false,
@@ -296,8 +306,9 @@ fn block_can_be_flattened<'a>(
expr: &'a ast::Expr,
) -> Option<&'a ast::Block> {
match expr.kind {
- ast::ExprKind::Block(ref block, _)
- if !is_unsafe_block(block)
+ ast::ExprKind::Block(ref block, label)
+ if label.is_none()
+ && !is_unsafe_block(block)
&& !context.inside_macro()
&& is_simple_block(context, block, Some(&expr.attrs))
&& !stmt_is_expr_mac(&block.stmts[0]) =>
@@ -532,7 +543,7 @@ fn rewrite_guard(
if let Some(cond_shape) = cond_shape {
if let Some(cond_str) = guard.rewrite(context, cond_shape) {
if !cond_str.contains('\n') || pattern_width <= context.config.tab_spaces() {
- return Some(format!(" if {}", cond_str));
+ return Some(format!(" if {cond_str}"));
}
}
}
diff --git a/src/tools/rustfmt/src/pairs.rs b/src/tools/rustfmt/src/pairs.rs
index d135da7e3..07c051937 100644
--- a/src/tools/rustfmt/src/pairs.rs
+++ b/src/tools/rustfmt/src/pairs.rs
@@ -42,9 +42,13 @@ pub(crate) fn rewrite_all_pairs(
context: &RewriteContext<'_>,
) -> Option<String> {
expr.flatten(context, shape).and_then(|list| {
- // First we try formatting on one line.
- rewrite_pairs_one_line(&list, shape, context)
- .or_else(|| rewrite_pairs_multiline(&list, shape, context))
+ if list.let_chain_count() > 0 && !list.can_rewrite_let_chain_single_line() {
+ rewrite_pairs_multiline(&list, shape, context)
+ } else {
+ // First we try formatting on one line.
+ rewrite_pairs_one_line(&list, shape, context)
+ .or_else(|| rewrite_pairs_multiline(&list, shape, context))
+ }
})
}
@@ -234,8 +238,8 @@ where
let rhs_result = rhs.rewrite(context, rhs_shape)?;
let indent_str = rhs_shape.indent.to_string_with_newline(context.config);
let infix_with_sep = match separator_place {
- SeparatorPlace::Back => format!("{}{}", infix, indent_str),
- SeparatorPlace::Front => format!("{}{}", indent_str, infix),
+ SeparatorPlace::Back => format!("{infix}{indent_str}"),
+ SeparatorPlace::Front => format!("{indent_str}{infix}"),
};
Some(format!(
"{}{}{}{}",
@@ -255,6 +259,37 @@ struct PairList<'a, 'b, T: Rewrite> {
separators: Vec<&'a str>,
}
+fn is_ident(expr: &ast::Expr) -> bool {
+ match &expr.kind {
+ ast::ExprKind::Path(None, path) if path.segments.len() == 1 => true,
+ ast::ExprKind::Unary(_, expr)
+ | ast::ExprKind::AddrOf(_, _, expr)
+ | ast::ExprKind::Paren(expr)
+ | ast::ExprKind::Try(expr) => is_ident(expr),
+ _ => false,
+ }
+}
+
+impl<'a, 'b> PairList<'a, 'b, ast::Expr> {
+ fn let_chain_count(&self) -> usize {
+ self.list
+ .iter()
+ .filter(|(expr, _)| matches!(expr.kind, ast::ExprKind::Let(..)))
+ .count()
+ }
+
+ fn can_rewrite_let_chain_single_line(&self) -> bool {
+ if self.list.len() != 2 {
+ return false;
+ }
+
+ let fist_item_is_ident = is_ident(self.list[0].0);
+ let second_item_is_let_chain = matches!(self.list[1].0.kind, ast::ExprKind::Let(..));
+
+ fist_item_is_ident && second_item_is_let_chain
+ }
+}
+
impl FlattenPair for ast::Expr {
fn flatten(
&self,
diff --git a/src/tools/rustfmt/src/parse/session.rs b/src/tools/rustfmt/src/parse/session.rs
index 3f94bb299..0573df9de 100644
--- a/src/tools/rustfmt/src/parse/session.rs
+++ b/src/tools/rustfmt/src/parse/session.rs
@@ -316,8 +316,7 @@ impl LineRangeUtils for ParseSess {
debug_assert_eq!(
lo.sf.name, hi.sf.name,
- "span crossed file boundary: lo: {:?}, hi: {:?}",
- lo, hi
+ "span crossed file boundary: lo: {lo:?}, hi: {hi:?}"
);
// in case the span starts with a newline, the line range is off by 1 without the
diff --git a/src/tools/rustfmt/src/patterns.rs b/src/tools/rustfmt/src/patterns.rs
index 3f3351725..33f3b4b8a 100644
--- a/src/tools/rustfmt/src/patterns.rs
+++ b/src/tools/rustfmt/src/patterns.rs
@@ -208,7 +208,7 @@ impl Rewrite for Pat {
None => "",
Some(_) => " ",
};
- format!("{}{}{}", lhs_spacing, infix, rhs_spacing)
+ format!("{lhs_spacing}{infix}{rhs_spacing}")
} else {
infix.to_owned()
};
@@ -283,7 +283,7 @@ fn rewrite_struct_pat(
let path_str = rewrite_path(context, PathContext::Expr, qself, path, path_shape)?;
if fields.is_empty() && !ellipsis {
- return Some(format!("{} {{}}", path_str));
+ return Some(format!("{path_str} {{}}"));
}
let (ellipsis_str, terminator) = if ellipsis { (", ..", "..") } else { ("", "}") };
@@ -344,7 +344,7 @@ fn rewrite_struct_pat(
// ast::Pat doesn't have attrs so use &[]
let fields_str = wrap_struct_field(context, &[], &fields_str, shape, v_shape, one_line_width)?;
- Some(format!("{} {{{}}}", path_str, fields_str))
+ Some(format!("{path_str} {{{fields_str}}}"))
}
impl Rewrite for PatField {
@@ -376,7 +376,7 @@ impl Rewrite for PatField {
let id_str = rewrite_ident(context, self.ident);
let one_line_width = id_str.len() + 2 + pat_str.len();
let pat_and_id_str = if one_line_width <= shape.width {
- format!("{}: {}", id_str, pat_str)
+ format!("{id_str}: {pat_str}")
} else {
format!(
"{}:\n{}{}",
diff --git a/src/tools/rustfmt/src/rustfmt_diff.rs b/src/tools/rustfmt/src/rustfmt_diff.rs
index 1724a0f87..c98834521 100644
--- a/src/tools/rustfmt/src/rustfmt_diff.rs
+++ b/src/tools/rustfmt/src/rustfmt_diff.rs
@@ -95,7 +95,7 @@ impl fmt::Display for ModifiedLines {
)?;
for line in &chunk.lines {
- writeln!(f, "{}", line)?;
+ writeln!(f, "{line}")?;
}
}
@@ -166,12 +166,12 @@ impl OutputWriter {
if let Some(color) = color {
t.fg(color).unwrap();
}
- writeln!(t, "{}", msg).unwrap();
+ writeln!(t, "{msg}").unwrap();
if color.is_some() {
t.reset().unwrap();
}
}
- None => println!("{}", msg),
+ None => println!("{msg}"),
}
}
}
@@ -265,16 +265,15 @@ where
for line in mismatch.lines {
match line {
DiffLine::Context(ref str) => {
- writer.writeln(&format!(" {}{}", str, line_terminator), None)
+ writer.writeln(&format!(" {str}{line_terminator}"), None)
}
DiffLine::Expected(ref str) => writer.writeln(
- &format!("+{}{}", str, line_terminator),
+ &format!("+{str}{line_terminator}"),
Some(term::color::GREEN),
),
- DiffLine::Resulting(ref str) => writer.writeln(
- &format!("-{}{}", str, line_terminator),
- Some(term::color::RED),
- ),
+ DiffLine::Resulting(ref str) => {
+ writer.writeln(&format!("-{str}{line_terminator}"), Some(term::color::RED))
+ }
}
}
}
diff --git a/src/tools/rustfmt/src/skip.rs b/src/tools/rustfmt/src/skip.rs
index 68f85b2ad..d733f7068 100644
--- a/src/tools/rustfmt/src/skip.rs
+++ b/src/tools/rustfmt/src/skip.rs
@@ -105,7 +105,7 @@ pub(crate) fn is_skip_attr(segments: &[ast::PathSegment]) -> bool {
fn get_skip_names(kind: &str, attrs: &[ast::Attribute]) -> Vec<String> {
let mut skip_names = vec![];
- let path = format!("{}::{}::{}", RUSTFMT, SKIP, kind);
+ let path = format!("{RUSTFMT}::{SKIP}::{kind}");
for attr in attrs {
// rustc_ast::ast::Path is implemented partialEq
// but it is designed for segments.len() == 1
diff --git a/src/tools/rustfmt/src/source_file.rs b/src/tools/rustfmt/src/source_file.rs
index 56d4ab400..958f9b015 100644
--- a/src/tools/rustfmt/src/source_file.rs
+++ b/src/tools/rustfmt/src/source_file.rs
@@ -62,7 +62,7 @@ where
fn ensure_real_path(filename: &FileName) -> &Path {
match *filename {
FileName::Real(ref path) => path,
- _ => panic!("cannot format `{}` and emit to files", filename),
+ _ => panic!("cannot format `{filename}` and emit to files"),
}
}
diff --git a/src/tools/rustfmt/src/stmt.rs b/src/tools/rustfmt/src/stmt.rs
index 0b3854425..e3fe4ebca 100644
--- a/src/tools/rustfmt/src/stmt.rs
+++ b/src/tools/rustfmt/src/stmt.rs
@@ -3,7 +3,7 @@ use rustc_span::Span;
use crate::comment::recover_comment_removed;
use crate::config::Version;
-use crate::expr::{format_expr, ExprType};
+use crate::expr::{format_expr, is_simple_block, ExprType};
use crate::rewrite::{Rewrite, RewriteContext};
use crate::shape::Shape;
use crate::source_map::LineRangeUtils;
@@ -33,6 +33,21 @@ impl<'a> Stmt<'a> {
}
}
+ pub(crate) fn from_simple_block(
+ context: &RewriteContext<'_>,
+ block: &'a ast::Block,
+ attrs: Option<&[ast::Attribute]>,
+ ) -> Option<Self> {
+ if is_simple_block(context, block, attrs) {
+ let inner = &block.stmts[0];
+ // Simple blocks only contain one expr and no stmts
+ let is_last = true;
+ Some(Stmt { inner, is_last })
+ } else {
+ None
+ }
+ }
+
pub(crate) fn from_ast_node(inner: &'a ast::Stmt, is_last: bool) -> Self {
Stmt { inner, is_last }
}
@@ -80,13 +95,13 @@ impl<'a> Rewrite for Stmt<'a> {
} else {
ExprType::Statement
};
- format_stmt(context, shape, self.as_ast_node(), expr_type)
- }
-}
-
-impl Rewrite for ast::Stmt {
- fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
- format_stmt(context, shape, self, ExprType::Statement)
+ format_stmt(
+ context,
+ shape,
+ self.as_ast_node(),
+ expr_type,
+ self.is_last_expr(),
+ )
}
}
@@ -95,13 +110,14 @@ fn format_stmt(
shape: Shape,
stmt: &ast::Stmt,
expr_type: ExprType,
+ is_last_expr: bool,
) -> Option<String> {
skip_out_of_file_lines_range!(context, stmt.span());
let result = match stmt.kind {
ast::StmtKind::Local(ref local) => local.rewrite(context, shape),
ast::StmtKind::Expr(ref ex) | ast::StmtKind::Semi(ref ex) => {
- let suffix = if semicolon_for_stmt(context, stmt) {
+ let suffix = if semicolon_for_stmt(context, stmt, is_last_expr) {
";"
} else {
""
diff --git a/src/tools/rustfmt/src/string.rs b/src/tools/rustfmt/src/string.rs
index 78b72a50c..cb666fff6 100644
--- a/src/tools/rustfmt/src/string.rs
+++ b/src/tools/rustfmt/src/string.rs
@@ -1,7 +1,7 @@
// Format string literals.
use regex::Regex;
-use unicode_categories::UnicodeCategories;
+use unicode_properties::{GeneralCategory, UnicodeGeneralCategory};
use unicode_segmentation::UnicodeSegmentation;
use crate::config::Config;
@@ -366,7 +366,7 @@ fn is_whitespace(grapheme: &str) -> bool {
fn is_punctuation(grapheme: &str) -> bool {
grapheme
.chars()
- .all(UnicodeCategories::is_punctuation_other)
+ .all(|c| c.general_category() == GeneralCategory::OtherPunctuation)
}
fn graphemes_width(graphemes: &[&str]) -> usize {
diff --git a/src/tools/rustfmt/src/test/configuration_snippet.rs b/src/tools/rustfmt/src/test/configuration_snippet.rs
index c70b3c5fa..80b61c88a 100644
--- a/src/tools/rustfmt/src/test/configuration_snippet.rs
+++ b/src/tools/rustfmt/src/test/configuration_snippet.rs
@@ -233,13 +233,11 @@ impl ConfigCodeBlock {
Some(ConfigurationSection::ConfigName(name)) => {
assert!(
Config::is_valid_name(&name),
- "an unknown configuration option was found: {}",
- name
+ "an unknown configuration option was found: {name}"
);
assert!(
hash_set.remove(&name),
- "multiple configuration guides found for option {}",
- name
+ "multiple configuration guides found for option {name}"
);
code_block.set_config_name(Some(name));
}
@@ -266,7 +264,7 @@ fn configuration_snippet_tests() {
// Display results.
println!("Ran {} configurations tests.", blocks.len());
- assert_eq!(failures, 0, "{} configurations tests failed", failures);
+ assert_eq!(failures, 0, "{failures} configurations tests failed");
}
// Read Configurations.md and build a `Vec` of `ConfigCodeBlock` structs with one
@@ -289,7 +287,7 @@ fn get_code_blocks() -> Vec<ConfigCodeBlock> {
for name in hash_set {
if !Config::is_hidden_option(&name) {
- panic!("{} does not have a configuration guide", name);
+ panic!("{name} does not have a configuration guide");
}
}
diff --git a/src/tools/rustfmt/src/test/mod.rs b/src/tools/rustfmt/src/test/mod.rs
index 37854ead2..47f89c187 100644
--- a/src/tools/rustfmt/src/test/mod.rs
+++ b/src/tools/rustfmt/src/test/mod.rs
@@ -47,7 +47,7 @@ const FILE_SKIP_LIST: &[&str] = &[
];
fn init_log() {
- let _ = env_logger::builder().is_test(true).try_init();
+ let _ = tracing_subscriber::fmt().with_test_writer().try_init();
}
struct TestSetting {
@@ -203,8 +203,8 @@ fn coverage_tests() {
let files = get_test_files(Path::new("tests/coverage/source"), true);
let (_reports, count, fails) = check_files(files, &None);
- println!("Ran {} tests in coverage mode.", count);
- assert_eq!(fails, 0, "{} tests failed", fails);
+ println!("Ran {count} tests in coverage mode.");
+ assert_eq!(fails, 0, "{fails} tests failed");
}
#[test]
@@ -396,8 +396,8 @@ fn self_tests() {
let mut warnings = 0;
// Display results.
- println!("Ran {} self tests.", count);
- assert_eq!(fails, 0, "{} self tests failed", fails);
+ println!("Ran {count} self tests.");
+ assert_eq!(fails, 0, "{fails} self tests failed");
for format_report in reports {
println!(
@@ -407,11 +407,7 @@ fn self_tests() {
warnings += format_report.warning_count();
}
- assert_eq!(
- warnings, 0,
- "Rustfmt's code generated {} warnings",
- warnings
- );
+ assert_eq!(warnings, 0, "Rustfmt's code generated {warnings} warnings");
}
#[test]
@@ -606,7 +602,7 @@ fn stdin_handles_mod_inner_ignore_attr() {
fn format_lines_errors_are_reported() {
init_log();
let long_identifier = String::from_utf8(vec![b'a'; 239]).unwrap();
- let input = Input::Text(format!("fn {}() {{}}", long_identifier));
+ let input = Input::Text(format!("fn {long_identifier}() {{}}"));
let mut config = Config::default();
config.set().error_on_line_overflow(true);
let mut session = Session::<io::Stdout>::new(config, None);
@@ -618,7 +614,7 @@ fn format_lines_errors_are_reported() {
fn format_lines_errors_are_reported_with_tabs() {
init_log();
let long_identifier = String::from_utf8(vec![b'a'; 97]).unwrap();
- let input = Input::Text(format!("fn a() {{\n\t{}\n}}", long_identifier));
+ let input = Input::Text(format!("fn a() {{\n\t{long_identifier}\n}}"));
let mut config = Config::default();
config.set().error_on_line_overflow(true);
config.set().hard_tabs(true);
@@ -829,11 +825,11 @@ fn handle_result(
for (file_name, fmt_text) in result {
// If file is in tests/source, compare to file with same name in tests/target.
let target = get_target(&file_name, target);
- let open_error = format!("couldn't open target {:?}", target);
+ let open_error = format!("couldn't open target {target:?}");
let mut f = fs::File::open(&target).expect(&open_error);
let mut text = String::new();
- let read_error = format!("failed reading target {:?}", target);
+ let read_error = format!("failed reading target {target:?}");
f.read_to_string(&mut text).expect(&read_error);
// Ignore LF and CRLF difference for Windows.
diff --git a/src/tools/rustfmt/src/types.rs b/src/tools/rustfmt/src/types.rs
index 5e8edd8f8..127aff913 100644
--- a/src/tools/rustfmt/src/types.rs
+++ b/src/tools/rustfmt/src/types.rs
@@ -301,7 +301,7 @@ where
let output = match *output {
FnRetTy::Ty(ref ty) => {
let type_str = ty.rewrite(context, ty_shape)?;
- format!(" -> {}", type_str)
+ format!(" -> {type_str}")
}
FnRetTy::Default(..) => String::new(),
};
@@ -373,7 +373,7 @@ where
|| !context.use_block_indent()
|| is_inputs_empty
{
- format!("({})", list_str)
+ format!("({list_str})")
} else {
format!(
"({}{}{})",
@@ -383,7 +383,7 @@ where
)
};
if output.is_empty() || last_line_width(&args) + first_line_width(&output) <= shape.width {
- Some(format!("{}{}", args, output))
+ Some(format!("{args}{output}"))
} else {
Some(format!(
"{}\n{}{}",
@@ -426,12 +426,12 @@ impl Rewrite for ast::WherePredicate {
}) => {
let type_str = bounded_ty.rewrite(context, shape)?;
let colon = type_bound_colon(context).trim_end();
- let lhs = if let Some(lifetime_str) =
- rewrite_lifetime_param(context, shape, bound_generic_params)
+ let lhs = if let Some(binder_str) =
+ rewrite_bound_params(context, shape, bound_generic_params)
{
- format!("for<{}> {}{}", lifetime_str, type_str, colon)
+ format!("for<{binder_str}> {type_str}{colon}")
} else {
- format!("{}{}", type_str, colon)
+ format!("{type_str}{colon}")
};
rewrite_assign_rhs(context, lhs, bounds, &RhsAssignKind::Bounds, shape)?
@@ -657,8 +657,7 @@ impl Rewrite for ast::GenericParam {
impl Rewrite for ast::PolyTraitRef {
fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
- if let Some(lifetime_str) =
- rewrite_lifetime_param(context, shape, &self.bound_generic_params)
+ if let Some(lifetime_str) = rewrite_bound_params(context, shape, &self.bound_generic_params)
{
// 6 is "for<> ".len()
let extra_offset = lifetime_str.len() + 6;
@@ -666,7 +665,7 @@ impl Rewrite for ast::PolyTraitRef {
.trait_ref
.rewrite(context, shape.offset_left(extra_offset)?)?;
- Some(format!("for<{}> {}", lifetime_str, path_str))
+ Some(format!("for<{lifetime_str}> {path_str}"))
} else {
self.trait_ref.rewrite(context, shape)
}
@@ -684,9 +683,11 @@ impl Rewrite for ast::Ty {
match self.kind {
ast::TyKind::TraitObject(ref bounds, tobj_syntax) => {
// we have to consider 'dyn' keyword is used or not!!!
- let is_dyn = tobj_syntax == ast::TraitObjectSyntax::Dyn;
- // 4 is length of 'dyn '
- let shape = if is_dyn { shape.offset_left(4)? } else { shape };
+ let (shape, prefix) = match tobj_syntax {
+ ast::TraitObjectSyntax::Dyn => (shape.offset_left(4)?, "dyn "),
+ ast::TraitObjectSyntax::DynStar => (shape.offset_left(5)?, "dyn* "),
+ ast::TraitObjectSyntax::None => (shape, ""),
+ };
let mut res = bounds.rewrite(context, shape)?;
// We may have falsely removed a trailing `+` inside macro call.
if context.inside_macro() && bounds.len() == 1 {
@@ -694,11 +695,7 @@ impl Rewrite for ast::Ty {
res.push('+');
}
}
- if is_dyn {
- Some(format!("dyn {}", res))
- } else {
- Some(res)
- }
+ Some(format!("{prefix}{res}"))
}
ast::TyKind::Ptr(ref mt) => {
let prefix = match mt.mutbl {
@@ -794,7 +791,7 @@ impl Rewrite for ast::Ty {
if let Some(sh) = shape.sub_width(2) {
if let Some(ref s) = ty.rewrite(context, sh) {
if !s.contains('\n') {
- return Some(format!("({})", s));
+ return Some(format!("({s})"));
}
}
}
@@ -883,8 +880,7 @@ fn rewrite_bare_fn(
let mut result = String::with_capacity(128);
- if let Some(ref lifetime_str) = rewrite_lifetime_param(context, shape, &bare_fn.generic_params)
- {
+ if let Some(ref lifetime_str) = rewrite_bound_params(context, shape, &bare_fn.generic_params) {
result.push_str("for<");
// 6 = "for<> ".len(), 4 = "for<".
// This doesn't work out so nicely for multiline situation with lots of
@@ -898,7 +894,6 @@ fn rewrite_bare_fn(
result.push_str(&format_extern(
bare_fn.ext,
context.config.force_explicit_abi(),
- false,
));
result.push_str("fn");
@@ -1124,16 +1119,15 @@ pub(crate) fn can_be_overflowed_type(
}
}
-/// Returns `None` if there is no `LifetimeDef` in the given generic parameters.
-pub(crate) fn rewrite_lifetime_param(
+/// Returns `None` if there is no `GenericParam` in the list
+pub(crate) fn rewrite_bound_params(
context: &RewriteContext<'_>,
shape: Shape,
generic_params: &[ast::GenericParam],
) -> Option<String> {
let result = generic_params
.iter()
- .filter(|p| matches!(p.kind, ast::GenericParamKind::Lifetime))
- .map(|lt| lt.rewrite(context, shape))
+ .map(|param| param.rewrite(context, shape))
.collect::<Option<Vec<_>>>()?
.join(", ");
if result.is_empty() {
diff --git a/src/tools/rustfmt/src/utils.rs b/src/tools/rustfmt/src/utils.rs
index 4fc5a9b68..fd49030bf 100644
--- a/src/tools/rustfmt/src/utils.rs
+++ b/src/tools/rustfmt/src/utils.rs
@@ -69,7 +69,7 @@ pub(crate) fn format_visibility(
let path = segments_iter.collect::<Vec<_>>().join("::");
let in_str = if is_keyword(&path) { "" } else { "in " };
- Cow::from(format!("pub({}{}) ", in_str, path))
+ Cow::from(format!("pub({in_str}{path}) "))
}
}
}
@@ -131,23 +131,18 @@ pub(crate) fn format_mutability(mutability: ast::Mutability) -> &'static str {
}
#[inline]
-pub(crate) fn format_extern(
- ext: ast::Extern,
- explicit_abi: bool,
- is_mod: bool,
-) -> Cow<'static, str> {
- let abi = match ext {
- ast::Extern::None => "Rust".to_owned(),
- ast::Extern::Implicit(_) => "C".to_owned(),
- ast::Extern::Explicit(abi, _) => abi.symbol_unescaped.to_string(),
- };
-
- if abi == "Rust" && !is_mod {
- Cow::from("")
- } else if abi == "C" && !explicit_abi {
- Cow::from("extern ")
- } else {
- Cow::from(format!(r#"extern "{}" "#, abi))
+pub(crate) fn format_extern(ext: ast::Extern, explicit_abi: bool) -> Cow<'static, str> {
+ match ext {
+ ast::Extern::None => Cow::from(""),
+ ast::Extern::Implicit(_) if explicit_abi => Cow::from("extern \"C\" "),
+ ast::Extern::Implicit(_) => Cow::from("extern "),
+ // turn `extern "C"` into `extern` when `explicit_abi` is set to false
+ ast::Extern::Explicit(abi, _) if abi.symbol_unescaped == sym::C && !explicit_abi => {
+ Cow::from("extern ")
+ }
+ ast::Extern::Explicit(abi, _) => {
+ Cow::from(format!(r#"extern "{}" "#, abi.symbol_unescaped))
+ }
}
}
@@ -292,14 +287,20 @@ pub(crate) fn semicolon_for_expr(context: &RewriteContext<'_>, expr: &ast::Expr)
}
#[inline]
-pub(crate) fn semicolon_for_stmt(context: &RewriteContext<'_>, stmt: &ast::Stmt) -> bool {
+pub(crate) fn semicolon_for_stmt(
+ context: &RewriteContext<'_>,
+ stmt: &ast::Stmt,
+ is_last_expr: bool,
+) -> bool {
match stmt.kind {
ast::StmtKind::Semi(ref expr) => match expr.kind {
ast::ExprKind::While(..) | ast::ExprKind::Loop(..) | ast::ExprKind::ForLoop(..) => {
false
}
ast::ExprKind::Break(..) | ast::ExprKind::Continue(..) | ast::ExprKind::Ret(..) => {
- context.config.trailing_semicolon()
+ // The only time we can skip the semi-colon is if the config option is set to false
+ // **and** this is the last expr (even though any following exprs are unreachable)
+ context.config.trailing_semicolon() || !is_last_expr
}
_ => true,
},
@@ -472,7 +473,7 @@ pub(crate) fn is_block_expr(context: &RewriteContext<'_>, expr: &ast::Expr, repr
| ast::ExprKind::If(..)
| ast::ExprKind::Block(..)
| ast::ExprKind::ConstBlock(..)
- | ast::ExprKind::Async(..)
+ | ast::ExprKind::Gen(..)
| ast::ExprKind::Loop(..)
| ast::ExprKind::ForLoop(..)
| ast::ExprKind::TryBlock(..)
diff --git a/src/tools/rustfmt/tests/cargo-fmt/main.rs b/src/tools/rustfmt/tests/cargo-fmt/main.rs
index 701c36fad..63573bf34 100644
--- a/src/tools/rustfmt/tests/cargo-fmt/main.rs
+++ b/src/tools/rustfmt/tests/cargo-fmt/main.rs
@@ -26,7 +26,7 @@ fn cargo_fmt(args: &[&str]) -> (String, String) {
String::from_utf8(output.stdout).expect("utf-8"),
String::from_utf8(output.stderr).expect("utf-8"),
),
- Err(e) => panic!("failed to run `{:?} {:?}`: {}", cmd, args, e),
+ Err(e) => panic!("failed to run `{cmd:?} {args:?}`: {e}"),
}
}
diff --git a/src/tools/rustfmt/tests/config/issue-5816.toml b/src/tools/rustfmt/tests/config/issue-5816.toml
new file mode 100644
index 000000000..00375746e
--- /dev/null
+++ b/src/tools/rustfmt/tests/config/issue-5816.toml
@@ -0,0 +1 @@
+skip_macro_invocations=["*", "println"]
diff --git a/src/tools/rustfmt/tests/rustfmt/main.rs b/src/tools/rustfmt/tests/rustfmt/main.rs
index 4936a7174..7dcf7c841 100644
--- a/src/tools/rustfmt/tests/rustfmt/main.rs
+++ b/src/tools/rustfmt/tests/rustfmt/main.rs
@@ -27,7 +27,7 @@ fn rustfmt(args: &[&str]) -> (String, String) {
String::from_utf8(output.stdout).expect("utf-8"),
String::from_utf8(output.stderr).expect("utf-8"),
),
- Err(e) => panic!("failed to run `{:?} {:?}`: {}", cmd, args, e),
+ Err(e) => panic!("failed to run `{cmd:?} {args:?}`: {e}"),
}
}
@@ -71,9 +71,7 @@ fn print_config() {
]);
assert!(
Path::new("minimal-config").exists(),
- "stdout:\n{}\nstderr:\n{}",
- stdout,
- stderr
+ "stdout:\n{stdout}\nstderr:\n{stderr}"
);
remove_file("minimal-config").unwrap();
}
diff --git a/src/tools/rustfmt/tests/source/immovable_generators.rs b/src/tools/rustfmt/tests/source/immovable_coroutines.rs
index c57a1e144..3b94af0c9 100644
--- a/src/tools/rustfmt/tests/source/immovable_generators.rs
+++ b/src/tools/rustfmt/tests/source/immovable_coroutines.rs
@@ -1,4 +1,4 @@
-#![feature(generators)]
+#![feature(coroutines)]
unsafe fn foo() {
let mut ga = static || {
diff --git a/src/tools/rustfmt/tests/source/issue-3984.rs b/src/tools/rustfmt/tests/source/issue-3984.rs
new file mode 100644
index 000000000..c9bcfa406
--- /dev/null
+++ b/src/tools/rustfmt/tests/source/issue-3984.rs
@@ -0,0 +1,12 @@
+use a::{item /* comment */};
+use b::{
+ a,
+ // comment
+ item,
+};
+use c::item /* comment */;
+use d::item; // really long comment (with `use` exactly 100 characters) ____________________________
+
+use std::e::{/* it's a comment! */ bar /* and another */};
+use std::f::{/* it's a comment! */ bar};
+use std::g::{bar /* and another */};
diff --git a/src/tools/rustfmt/tests/source/issue-4808.rs b/src/tools/rustfmt/tests/source/issue-4808.rs
new file mode 100644
index 000000000..93076edcd
--- /dev/null
+++ b/src/tools/rustfmt/tests/source/issue-4808.rs
@@ -0,0 +1,13 @@
+trait Trait {
+ fn method(&self) {}
+}
+
+impl<F: Fn() -> T, T> Trait for F {}
+
+impl Trait for f32 {}
+
+fn main() {
+ || 10. .method();
+ || .. .method();
+ || 1.. .method();
+}
diff --git a/src/tools/rustfmt/tests/source/issue-5655/one.rs b/src/tools/rustfmt/tests/source/issue-5655/one.rs
new file mode 100644
index 000000000..1758ec56f
--- /dev/null
+++ b/src/tools/rustfmt/tests/source/issue-5655/one.rs
@@ -0,0 +1,9 @@
+// rustfmt-version: One
+
+fn foo<T>(_: T)
+where
+ T: std::fmt::Debug,
+
+ T: std::fmt::Display,
+{
+}
diff --git a/src/tools/rustfmt/tests/source/issue-5655/two.rs b/src/tools/rustfmt/tests/source/issue-5655/two.rs
new file mode 100644
index 000000000..e37ebbea8
--- /dev/null
+++ b/src/tools/rustfmt/tests/source/issue-5655/two.rs
@@ -0,0 +1,9 @@
+// rustfmt-version: Two
+
+fn foo<T>(_: T)
+where
+ T: std::fmt::Debug,
+
+ T: std::fmt::Display,
+{
+}
diff --git a/src/tools/rustfmt/tests/source/issue-5791.rs b/src/tools/rustfmt/tests/source/issue-5791.rs
new file mode 100644
index 000000000..40bc6daa9
--- /dev/null
+++ b/src/tools/rustfmt/tests/source/issue-5791.rs
@@ -0,0 +1,3 @@
+pub fn main() {
+ 0. .to_string();
+}
diff --git a/src/tools/rustfmt/tests/source/issue-5835.rs b/src/tools/rustfmt/tests/source/issue-5835.rs
new file mode 100644
index 000000000..3e4da3492
--- /dev/null
+++ b/src/tools/rustfmt/tests/source/issue-5835.rs
@@ -0,0 +1,8 @@
+// rustfmt-wrap_comments: true
+
+/// . a
+pub fn foo() {}
+
+pub fn main() {
+ // . a
+}
diff --git a/src/tools/rustfmt/tests/source/issue-5852/default.rs b/src/tools/rustfmt/tests/source/issue-5852/default.rs
new file mode 100644
index 000000000..df84f8f58
--- /dev/null
+++ b/src/tools/rustfmt/tests/source/issue-5852/default.rs
@@ -0,0 +1,104 @@
+use std::{
+ fs,
+ // (temporarily commented, we'll need this again in a second) io,
+};
+
+use foo::{
+ self // this is important
+};
+
+use foo :: bar
+;
+
+use foo::{bar};
+
+use foo::{
+ bar
+ // abc
+};
+
+use foo::{
+ bar,
+ // abc
+};
+
+use foo::{
+ // 345
+ bar
+};
+
+use foo::{
+ self
+ // abc
+};
+
+use foo::{
+ self,
+ // abc
+};
+
+use foo::{
+ // 345
+ self
+};
+
+use foo::{
+ self // a
+ ,
+};
+
+use foo::{ self /* a */ };
+
+use foo::{ self /* a */, };
+
+use foo::{
+ // abc
+ abc::{
+ xyz
+ // 123
+ }
+};
+
+use foo::{
+ // abc
+ bar,
+ abc
+};
+
+use foo::{
+ bar,
+ // abc
+ abc
+};
+
+use foo::{
+ bar,
+ abc
+ // abc
+};
+
+use foo::{
+ bar,
+ abc,
+ // abc
+};
+
+use foo::{
+ self,
+ // abc
+ abc::{
+ xyz
+ // 123
+ }
+};
+
+use foo::{
+ self,
+ // abc
+ abc::{
+ // 123
+ xyz
+ }
+};
+
+use path::{self /*comment*/,};
diff --git a/src/tools/rustfmt/tests/source/issue-5852/horizontal.rs b/src/tools/rustfmt/tests/source/issue-5852/horizontal.rs
new file mode 100644
index 000000000..63bfb7e57
--- /dev/null
+++ b/src/tools/rustfmt/tests/source/issue-5852/horizontal.rs
@@ -0,0 +1,106 @@
+// rustfmt-imports_layout: Horizontal
+
+use std::{
+ fs,
+ // (temporarily commented, we'll need this again in a second) io,
+};
+
+use foo::{
+ self // this is important
+};
+
+use foo :: bar
+;
+
+use foo::{bar};
+
+use foo::{
+ bar
+ // abc
+};
+
+use foo::{
+ bar,
+ // abc
+};
+
+use foo::{
+ // 345
+ bar
+};
+
+use foo::{
+ self
+ // abc
+};
+
+use foo::{
+ self,
+ // abc
+};
+
+use foo::{
+ // 345
+ self
+};
+
+use foo::{
+ self // a
+ ,
+};
+
+use foo::{ self /* a */ };
+
+use foo::{ self /* a */, };
+
+use foo::{
+ // abc
+ abc::{
+ xyz
+ // 123
+ }
+};
+
+use foo::{
+ // abc
+ bar,
+ abc
+};
+
+use foo::{
+ bar,
+ // abc
+ abc
+};
+
+use foo::{
+ bar,
+ abc
+ // abc
+};
+
+use foo::{
+ bar,
+ abc,
+ // abc
+};
+
+use foo::{
+ self,
+ // abc
+ abc::{
+ xyz
+ // 123
+ }
+};
+
+use foo::{
+ self,
+ // abc
+ abc::{
+ // 123
+ xyz
+ }
+};
+
+use path::{self /*comment*/,};
diff --git a/src/tools/rustfmt/tests/source/issue-5852/horizontal_vertical.rs b/src/tools/rustfmt/tests/source/issue-5852/horizontal_vertical.rs
new file mode 100644
index 000000000..3f3ce0669
--- /dev/null
+++ b/src/tools/rustfmt/tests/source/issue-5852/horizontal_vertical.rs
@@ -0,0 +1,106 @@
+// rustfmt-imports_layout: HorizontalVertical
+
+use std::{
+ fs,
+ // (temporarily commented, we'll need this again in a second) io,
+};
+
+use foo::{
+ self // this is important
+};
+
+use foo :: bar
+;
+
+use foo::{bar};
+
+use foo::{
+ bar
+ // abc
+};
+
+use foo::{
+ bar,
+ // abc
+};
+
+use foo::{
+ // 345
+ bar
+};
+
+use foo::{
+ self
+ // abc
+};
+
+use foo::{
+ self,
+ // abc
+};
+
+use foo::{
+ // 345
+ self
+};
+
+use foo::{
+ self // a
+ ,
+};
+
+use foo::{ self /* a */ };
+
+use foo::{ self /* a */, };
+
+use foo::{
+ // abc
+ abc::{
+ xyz
+ // 123
+ }
+};
+
+use foo::{
+ // abc
+ bar,
+ abc
+};
+
+use foo::{
+ bar,
+ // abc
+ abc
+};
+
+use foo::{
+ bar,
+ abc
+ // abc
+};
+
+use foo::{
+ bar,
+ abc,
+ // abc
+};
+
+use foo::{
+ self,
+ // abc
+ abc::{
+ xyz
+ // 123
+ }
+};
+
+use foo::{
+ self,
+ // abc
+ abc::{
+ // 123
+ xyz
+ }
+};
+
+use path::{self /*comment*/,};
diff --git a/src/tools/rustfmt/tests/source/issue-5852/issue_example.rs b/src/tools/rustfmt/tests/source/issue-5852/issue_example.rs
new file mode 100644
index 000000000..20c2b7640
--- /dev/null
+++ b/src/tools/rustfmt/tests/source/issue-5852/issue_example.rs
@@ -0,0 +1,8 @@
+use std::{
+ fs,
+ // (temporarily commented, we'll need this again in a second) io,
+};
+
+use foo::{
+ self // this is important
+};
diff --git a/src/tools/rustfmt/tests/source/issue-5852/split.rs b/src/tools/rustfmt/tests/source/issue-5852/split.rs
new file mode 100644
index 000000000..14e9ea1b6
--- /dev/null
+++ b/src/tools/rustfmt/tests/source/issue-5852/split.rs
@@ -0,0 +1,111 @@
+// rustfmt-imports_granularity: Item
+
+use std::{
+ fs,
+ // (temporarily commented, we'll need this again in a second) io,
+};
+
+use foo::{
+ self // this is important
+};
+
+use foo :: bar
+;
+
+use foo::{bar};
+
+use foo::{
+ bar
+ // abc
+};
+
+use foo::{
+ bar,
+ // abc
+};
+
+use foo::{
+ // 345
+ bar
+};
+
+use foo::{
+ self
+ // abc
+};
+
+use foo::{
+ self,
+ // abc
+};
+
+use foo::{
+ // 345
+ self
+};
+
+use foo::{
+ self // a
+ ,
+};
+
+use foo::{ self /* a */ };
+
+use foo::{ self /* a */, };
+
+use foo::{
+ // abc
+ abc::{
+ xyz
+ // 123
+ }
+};
+
+use foo::{
+ bar,
+ abc
+};
+
+use foo::{
+ // abc
+ bar,
+ abc
+};
+
+use foo::{
+ bar,
+ // abc
+ abc
+};
+
+use foo::{
+ bar,
+ abc
+ // abc
+};
+
+use foo::{
+ bar,
+ abc,
+ // abc
+};
+
+use foo::{
+ self,
+ // abc
+ abc::{
+ xyz
+ // 123
+ }
+};
+
+use foo::{
+ self,
+ // abc
+ abc::{
+ // 123
+ xyz
+ }
+};
+
+use path::{self /*comment*/,};
diff --git a/src/tools/rustfmt/tests/source/issue-5852/vertical.rs b/src/tools/rustfmt/tests/source/issue-5852/vertical.rs
new file mode 100644
index 000000000..b9ba99889
--- /dev/null
+++ b/src/tools/rustfmt/tests/source/issue-5852/vertical.rs
@@ -0,0 +1,106 @@
+// rustfmt-imports_layout: Vertical
+
+use std::{
+ fs,
+ // (temporarily commented, we'll need this again in a second) io,
+};
+
+use foo::{
+ self // this is important
+};
+
+use foo :: bar
+;
+
+use foo::{bar};
+
+use foo::{
+ bar
+ // abc
+};
+
+use foo::{
+ bar,
+ // abc
+};
+
+use foo::{
+ // 345
+ bar
+};
+
+use foo::{
+ self
+ // abc
+};
+
+use foo::{
+ self,
+ // abc
+};
+
+use foo::{
+ // 345
+ self
+};
+
+use foo::{
+ self // a
+ ,
+};
+
+use foo::{ self /* a */ };
+
+use foo::{ self /* a */, };
+
+use foo::{
+ // abc
+ abc::{
+ xyz
+ // 123
+ }
+};
+
+use foo::{
+ // abc
+ bar,
+ abc
+};
+
+use foo::{
+ bar,
+ // abc
+ abc
+};
+
+use foo::{
+ bar,
+ abc
+ // abc
+};
+
+use foo::{
+ bar,
+ abc,
+ // abc
+};
+
+use foo::{
+ self,
+ // abc
+ abc::{
+ xyz
+ // 123
+ }
+};
+
+use foo::{
+ self,
+ // abc
+ abc::{
+ // 123
+ xyz
+ }
+};
+
+use path::{self /*comment*/,};
diff --git a/src/tools/rustfmt/tests/source/issue-5935.rs b/src/tools/rustfmt/tests/source/issue-5935.rs
new file mode 100644
index 000000000..a1aac0562
--- /dev/null
+++ b/src/tools/rustfmt/tests/source/issue-5935.rs
@@ -0,0 +1,9 @@
+struct Regs<
+ const BEGIN: u64,
+ const END: u64,
+ const DIM: usize,
+ const N: usize = { (END - BEGIN) as usize / (8 * DIM) + 1 },
+>
+{
+ _foo: u64,
+} \ No newline at end of file
diff --git a/src/tools/rustfmt/tests/source/issue_5721.rs b/src/tools/rustfmt/tests/source/issue_5721.rs
new file mode 100644
index 000000000..e5ae9612c
--- /dev/null
+++ b/src/tools/rustfmt/tests/source/issue_5721.rs
@@ -0,0 +1,8 @@
+#![feature(non_lifetime_binders)]
+#![allow(incomplete_features)]
+
+trait Other<U: ?Sized> {}
+
+trait Trait<U>
+where
+ for<T> U: Other<T> {}
diff --git a/src/tools/rustfmt/tests/source/issue_5730.rs b/src/tools/rustfmt/tests/source/issue_5730.rs
new file mode 100644
index 000000000..9a3f4f0d0
--- /dev/null
+++ b/src/tools/rustfmt/tests/source/issue_5730.rs
@@ -0,0 +1,3 @@
+macro_rules! statement {
+ () => {;};
+}
diff --git a/src/tools/rustfmt/tests/source/issue_5735.rs b/src/tools/rustfmt/tests/source/issue_5735.rs
new file mode 100644
index 000000000..7708d028b
--- /dev/null
+++ b/src/tools/rustfmt/tests/source/issue_5735.rs
@@ -0,0 +1,6 @@
+fn find_errors(mut self) {
+ let errors: Vec<> = vec!{
+ #[debug_format = "A({})"]
+ struct A {}
+ };
+}
diff --git a/src/tools/rustfmt/tests/source/issue_5882.rs b/src/tools/rustfmt/tests/source/issue_5882.rs
new file mode 100644
index 000000000..e36f99654
--- /dev/null
+++ b/src/tools/rustfmt/tests/source/issue_5882.rs
@@ -0,0 +1,7 @@
+macro_rules!foo{}
+macro_rules!bar{/*comment*/}
+macro_rules!baz{//comment
+}
+macro_rules!foobar{
+//comment
+}
diff --git a/src/tools/rustfmt/tests/source/let_chains.rs b/src/tools/rustfmt/tests/source/let_chains.rs
new file mode 100644
index 000000000..b7c1f8110
--- /dev/null
+++ b/src/tools/rustfmt/tests/source/let_chains.rs
@@ -0,0 +1,121 @@
+fn main() {
+ if let x = x && x {}
+
+ if xxx && let x = x {}
+
+ if aaaaaaaaaaaaaaaaaaaaa && aaaaaaaaaaaaaaa && aaaaaaaaa && let Some(x) = xxxxxxxxxxxx && aaaaaaa && let None = aaaaaaaaaa {}
+
+ if aaaaaaaaaaaaaaaaaaaaa && aaaaaaaaaaaaaaa && aaaaaaaaa && let Some(x) = xxxxxxxxxxxx && aaaaaaa && let None = aaaaaaaaaa {}
+
+ if let Some(Struct { x:TS(1,2) }) = path::to::<_>(hehe)
+ && let [Simple, people] = /* get ready */ create_universe(/* hi */ GreatPowers).initialize_badminton().populate_swamps() &&
+ let everybody = (Loops { hi /*hi*/ , ..loopy() }) && summons::triumphantly() { todo!() }
+
+ if let XXXXXXXXX { xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx, yyyyyyyyyyyyy, zzzzzzzzzzzzz} = xxxxxxx()
+ && let Foo = bar() { todo!() }
+}
+
+fn test_single_line_let_chain() {
+ // first item in let-chain is an ident
+ if a && let Some(b) = foo() {
+ }
+
+ // first item in let-chain is a unary ! with an ident
+ let unary_not = if !from_hir_call
+ && let Some(p) = parent
+ {
+ };
+
+ // first item in let-chain is a unary * with an ident
+ let unary_deref = if *some_deref
+ && let Some(p) = parent
+ {
+ };
+
+ // first item in let-chain is a unary - (neg) with an ident
+ let unary_neg = if -some_ident
+ && let Some(p) = parent
+ {
+ };
+
+ // first item in let-chain is a try (?) with an ident
+ let try_ = if some_try?
+ && let Some(p) = parent
+ {
+ };
+
+ // first item in let-chain is an ident wrapped in parens
+ let in_parens = if (some_ident)
+ && let Some(p) = parent
+ {
+ };
+
+ // first item in let-chain is a ref & with an ident
+ let _ref = if &some_ref
+ && let Some(p) = parent
+ {
+ };
+
+ // first item in let-chain is a ref &mut with an ident
+ let mut_ref = if &mut some_ref
+ && let Some(p) = parent
+ {
+ };
+
+ // chain unary ref and try
+ let chain_of_unary_ref_and_try = if !&*some_ref?
+ && let Some(p) = parent {
+ };
+}
+
+fn test_multi_line_let_chain() {
+ // Can only single line the let-chain if the first item is an ident
+ if let Some(x) = y && a {
+
+ }
+
+ // More than one let-chain must be formatted on multiple lines
+ if let Some(x) = y && let Some(a) = b {
+
+ }
+
+ // The ident isn't long enough so we don't wrap the first let-chain
+ if a && let Some(x) = y && let Some(a) = b {
+
+ }
+
+ // The ident is long enough so both let-chains are wrapped
+ if aaa && let Some(x) = y && let Some(a) = b {
+
+ }
+
+ // function call
+ if a() && let Some(x) = y {
+
+ }
+
+ // bool literal
+ if true && let Some(x) = y {
+
+ }
+
+ // cast to a bool
+ if 1 as bool && let Some(x) = y {
+
+ }
+
+ // matches! macro call
+ if matches!(a, some_type) && let Some(x) = y {
+
+ }
+
+ // block expression returning bool
+ if { true } && let Some(x) = y {
+
+ }
+
+ // field access
+ if a.x && let Some(x) = y {
+
+ }
+}
diff --git a/src/tools/rustfmt/tests/source/let_else.rs b/src/tools/rustfmt/tests/source/let_else.rs
index 85b3604ad..cb2859e80 100644
--- a/src/tools/rustfmt/tests/source/let_else.rs
+++ b/src/tools/rustfmt/tests/source/let_else.rs
@@ -160,3 +160,23 @@ fn with_trailing_try_operator() {
// Maybe this is a workaround?
let Ok(Some(next_bucket)) = ranking_rules[cur_ranking_rule_index].next_bucket(ctx, logger, &ranking_rule_universes[cur_ranking_rule_index]) else { return };
}
+
+fn issue5901() {
+ #[cfg(target_os = "linux")]
+ let Some(x) = foo else { todo!() };
+
+ #[cfg(target_os = "linux")]
+ // Some comments between attributes and let-else statement
+ let Some(x) = foo else { todo!() };
+
+ #[cfg(target_os = "linux")]
+ #[cfg(target_arch = "x86_64")]
+ let Some(x) = foo else { todo!() };
+
+ // The else block will be multi-lined because attributes and comments before `let`
+ // are included when calculating max width
+ #[cfg(target_os = "linux")]
+ #[cfg(target_arch = "x86_64")]
+ // Some comments between attributes and let-else statement
+ let Some(x) = foo() else { todo!() };
+}
diff --git a/src/tools/rustfmt/tests/source/let_else_v2.rs b/src/tools/rustfmt/tests/source/let_else_v2.rs
new file mode 100644
index 000000000..a420fbcf9
--- /dev/null
+++ b/src/tools/rustfmt/tests/source/let_else_v2.rs
@@ -0,0 +1,56 @@
+// rustfmt-version: Two
+// rustfmt-single_line_let_else_max_width: 100
+
+fn issue5901() {
+ #[cfg(target_os = "linux")]
+ let Some(x) = foo else { todo!() };
+
+ #[cfg(target_os = "linux")]
+ // Some comments between attributes and let-else statement
+ let Some(x) = foo else { todo!() };
+
+ #[cfg(target_os = "linux")]
+ #[cfg(target_arch = "x86_64")]
+ let Some(x) = foo else { todo!() };
+
+ // The else block is multi-lined
+ #[cfg(target_os = "linux")]
+ let Some(x) = foo else { return; };
+
+ // The else block will be single-lined because attributes and comments before `let`
+ // are no longer included when calculating max width
+ #[cfg(target_os = "linux")]
+ #[cfg(target_arch = "x86_64")]
+ // Some comments between attributes and let-else statement
+ let Some(x) = foo else { todo!() };
+
+ // Some more test cases for v2 formatting with attributes
+
+ #[cfg(target_os = "linux")]
+ #[cfg(target_arch = "x86_64")]
+ let Some(x) = opt
+ // pre else keyword line-comment
+ else { return; };
+
+ #[cfg(target_os = "linux")]
+ #[cfg(target_arch = "x86_64")]
+ let Some(x) = opt else
+ // post else keyword line-comment
+ { return; };
+
+ #[cfg(target_os = "linux")]
+ #[cfg(target_arch = "x86_64")]
+ let Foo {x: Bar(..), y: FooBar(..), z: Baz(..)} = opt else {
+ return;
+ };
+
+ #[cfg(target_os = "linux")]
+ #[cfg(target_arch = "x86_64")]
+ let Some(Ok((Message::ChangeColor(super::color::Color::Rgb(r, g, b)), Point { x, y, z }))) = opt else {
+ return;
+ };
+
+ #[cfg(target_os = "linux")]
+ #[cfg(target_arch = "x86_64")]
+ let Some(x) = very_very_very_very_very_very_very_very_very_very_very_very_long_expression_in_assign_rhs() else { return; };
+}
diff --git a/src/tools/rustfmt/tests/source/match.rs b/src/tools/rustfmt/tests/source/match.rs
index b5dc9957a..d1d8d7f2c 100644
--- a/src/tools/rustfmt/tests/source/match.rs
+++ b/src/tools/rustfmt/tests/source/match.rs
@@ -292,6 +292,9 @@ fn guards() {
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
if fooooooooooooooooooooo &&
(bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb || cccccccccccccccccccccccccccccccccccccccc) => {}
+ Hi { friend } if let None = friend => {}
+ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa if let Some(foooooooooooooo) = hiiiiiiiiiiiiiii => {}
+ aaaaaaaaaaaaaaaaa if let Superman { powers: Some(goteem), .. } = all::get_random_being::<Super>() => {}
}
}
diff --git a/src/tools/rustfmt/tests/source/non-lifetime-binders.rs b/src/tools/rustfmt/tests/source/non-lifetime-binders.rs
new file mode 100644
index 000000000..c26393c8f
--- /dev/null
+++ b/src/tools/rustfmt/tests/source/non-lifetime-binders.rs
@@ -0,0 +1,10 @@
+fn main() where for<'a, T: Sized + 'a, const C: usize> [&'a T; C]: Sized {
+ let x = for<T>
+ || {};
+
+ let y: dyn
+ for<T> Into<T>;
+
+ let z: for<T>
+ fn(T);
+}
diff --git a/src/tools/rustfmt/tests/source/skip_macro_invocations/config_file.rs b/src/tools/rustfmt/tests/source/skip_macro_invocations/config_file.rs
new file mode 100644
index 000000000..e0f5ddf52
--- /dev/null
+++ b/src/tools/rustfmt/tests/source/skip_macro_invocations/config_file.rs
@@ -0,0 +1,9 @@
+// rustfmt-unstable: true
+// rustfmt-config: issue-5816.toml
+
+fn main() {
+ println!( "Hello, world!");
+ let x =
+7
+;
+}
diff --git a/src/tools/rustfmt/tests/target/extern-rust.rs b/src/tools/rustfmt/tests/target/extern-rust.rs
new file mode 100644
index 000000000..32824c912
--- /dev/null
+++ b/src/tools/rustfmt/tests/target/extern-rust.rs
@@ -0,0 +1 @@
+extern "Rust" fn uwu() {}
diff --git a/src/tools/rustfmt/tests/target/immovable_generators.rs b/src/tools/rustfmt/tests/target/immovable_coroutines.rs
index 0bf7a2d91..f52cfa00f 100644
--- a/src/tools/rustfmt/tests/target/immovable_generators.rs
+++ b/src/tools/rustfmt/tests/target/immovable_coroutines.rs
@@ -1,4 +1,4 @@
-#![feature(generators)]
+#![feature(coroutines)]
unsafe fn foo() {
let mut ga = static || {
diff --git a/src/tools/rustfmt/tests/target/issue-3984.rs b/src/tools/rustfmt/tests/target/issue-3984.rs
new file mode 100644
index 000000000..9e700c0f7
--- /dev/null
+++ b/src/tools/rustfmt/tests/target/issue-3984.rs
@@ -0,0 +1,12 @@
+use a::{item /* comment */};
+use b::{
+ a,
+ // comment
+ item,
+};
+use c::item; /* comment */
+use d::item; // really long comment (with `use` exactly 100 characters) ____________________________
+
+use std::e::{/* it's a comment! */ bar /* and another */};
+use std::f::{/* it's a comment! */ bar};
+use std::g::{bar /* and another */};
diff --git a/src/tools/rustfmt/tests/target/issue-4808.rs b/src/tools/rustfmt/tests/target/issue-4808.rs
new file mode 100644
index 000000000..cdef53a1b
--- /dev/null
+++ b/src/tools/rustfmt/tests/target/issue-4808.rs
@@ -0,0 +1,13 @@
+trait Trait {
+ fn method(&self) {}
+}
+
+impl<F: Fn() -> T, T> Trait for F {}
+
+impl Trait for f32 {}
+
+fn main() {
+ || (10.).method();
+ (|| ..).method();
+ (|| 1..).method();
+}
diff --git a/src/tools/rustfmt/tests/target/issue-5568.rs b/src/tools/rustfmt/tests/target/issue-5568.rs
new file mode 100644
index 000000000..03ca3a452
--- /dev/null
+++ b/src/tools/rustfmt/tests/target/issue-5568.rs
@@ -0,0 +1,14 @@
+// rustfmt-max_width: 119
+// rustfmt-format_code_in_doc_comments: true
+
+mod libs {
+ fn mrbgems_sources() {
+ [
+ "mrbgems/mruby-compiler/core/codegen.c", // Ruby parser and bytecode generation
+ "mrbgems/mruby-compiler/core/y.tab.c", // Ruby parser and bytecode generation
+ "mrbgems/mruby-metaprog/src/metaprog.c", // APIs on Kernel and Module for accessing classes and variables
+ "mrbgems/mruby-method/src/method.c", // `Method`, `UnboundMethod`, and method APIs on Kernel and Module
+ "mrbgems/mruby-pack/src/pack.c", // Array#pack and String#unpack
+ ]
+ }
+}
diff --git a/src/tools/rustfmt/tests/target/issue-5655/one.rs b/src/tools/rustfmt/tests/target/issue-5655/one.rs
new file mode 100644
index 000000000..1758ec56f
--- /dev/null
+++ b/src/tools/rustfmt/tests/target/issue-5655/one.rs
@@ -0,0 +1,9 @@
+// rustfmt-version: One
+
+fn foo<T>(_: T)
+where
+ T: std::fmt::Debug,
+
+ T: std::fmt::Display,
+{
+}
diff --git a/src/tools/rustfmt/tests/target/issue-5655/two.rs b/src/tools/rustfmt/tests/target/issue-5655/two.rs
new file mode 100644
index 000000000..14fbc3d13
--- /dev/null
+++ b/src/tools/rustfmt/tests/target/issue-5655/two.rs
@@ -0,0 +1,8 @@
+// rustfmt-version: Two
+
+fn foo<T>(_: T)
+where
+ T: std::fmt::Debug,
+ T: std::fmt::Display,
+{
+}
diff --git a/src/tools/rustfmt/tests/target/issue-5791.rs b/src/tools/rustfmt/tests/target/issue-5791.rs
new file mode 100644
index 000000000..3a44cf19a
--- /dev/null
+++ b/src/tools/rustfmt/tests/target/issue-5791.rs
@@ -0,0 +1,3 @@
+pub fn main() {
+ (0.).to_string();
+}
diff --git a/src/tools/rustfmt/tests/target/issue-5797/retain_trailing_semicolon.rs b/src/tools/rustfmt/tests/target/issue-5797/retain_trailing_semicolon.rs
new file mode 100644
index 000000000..851073971
--- /dev/null
+++ b/src/tools/rustfmt/tests/target/issue-5797/retain_trailing_semicolon.rs
@@ -0,0 +1,7 @@
+// rustfmt-trailing_semicolon: false
+
+fn foo() {}
+fn main() {
+ return;
+ foo()
+}
diff --git a/src/tools/rustfmt/tests/target/issue-5835.rs b/src/tools/rustfmt/tests/target/issue-5835.rs
new file mode 100644
index 000000000..3e4da3492
--- /dev/null
+++ b/src/tools/rustfmt/tests/target/issue-5835.rs
@@ -0,0 +1,8 @@
+// rustfmt-wrap_comments: true
+
+/// . a
+pub fn foo() {}
+
+pub fn main() {
+ // . a
+}
diff --git a/src/tools/rustfmt/tests/target/issue-5852/default.rs b/src/tools/rustfmt/tests/target/issue-5852/default.rs
new file mode 100644
index 000000000..a86872a68
--- /dev/null
+++ b/src/tools/rustfmt/tests/target/issue-5852/default.rs
@@ -0,0 +1,97 @@
+use std::{
+ fs,
+ // (temporarily commented, we'll need this again in a second) io,
+};
+
+use foo::{
+ self, // this is important
+};
+
+use foo::bar;
+
+use foo::bar;
+
+use foo::{
+ bar, // abc
+};
+
+use foo::{
+ bar,
+ // abc
+};
+
+use foo::{
+ // 345
+ bar,
+};
+
+use foo::{
+ self, // abc
+};
+
+use foo::{
+ self,
+ // abc
+};
+
+use foo::{
+ // 345
+ self,
+};
+
+use foo::{
+ self, // a
+};
+
+use foo::{self /* a */};
+
+use foo::{self /* a */};
+
+use foo::{
+ // abc
+ abc::{
+ xyz, // 123
+ },
+};
+
+use foo::{
+ abc,
+ // abc
+ bar,
+};
+
+use foo::{
+ // abc
+ abc,
+ bar,
+};
+
+use foo::{
+ abc, // abc
+ bar,
+};
+
+use foo::{
+ abc,
+ // abc
+ bar,
+};
+
+use foo::{
+ self,
+ // abc
+ abc::{
+ xyz, // 123
+ },
+};
+
+use foo::{
+ self,
+ // abc
+ abc::{
+ // 123
+ xyz,
+ },
+};
+
+use path::{self /*comment*/};
diff --git a/src/tools/rustfmt/tests/target/issue-5852/horizontal.rs b/src/tools/rustfmt/tests/target/issue-5852/horizontal.rs
new file mode 100644
index 000000000..017d83c9f
--- /dev/null
+++ b/src/tools/rustfmt/tests/target/issue-5852/horizontal.rs
@@ -0,0 +1,99 @@
+// rustfmt-imports_layout: Horizontal
+
+use std::{
+ fs,
+ // (temporarily commented, we'll need this again in a second) io,
+};
+
+use foo::{
+ self, // this is important
+};
+
+use foo::bar;
+
+use foo::bar;
+
+use foo::{
+ bar, // abc
+};
+
+use foo::{
+ bar,
+ // abc
+};
+
+use foo::{
+ // 345
+ bar,
+};
+
+use foo::{
+ self, // abc
+};
+
+use foo::{
+ self,
+ // abc
+};
+
+use foo::{
+ // 345
+ self,
+};
+
+use foo::{
+ self, // a
+};
+
+use foo::{self /* a */};
+
+use foo::{self /* a */};
+
+use foo::{
+ // abc
+ abc::{
+ xyz, // 123
+ },
+};
+
+use foo::{
+ abc,
+ // abc
+ bar,
+};
+
+use foo::{
+ // abc
+ abc,
+ bar,
+};
+
+use foo::{
+ abc, // abc
+ bar,
+};
+
+use foo::{
+ abc,
+ // abc
+ bar,
+};
+
+use foo::{
+ self,
+ // abc
+ abc::{
+ xyz, // 123
+ },
+};
+
+use foo::{
+ self,
+ // abc
+ abc::{
+ // 123
+ xyz,
+ },
+};
+
+use path::{self /*comment*/};
diff --git a/src/tools/rustfmt/tests/target/issue-5852/horizontal_vertical.rs b/src/tools/rustfmt/tests/target/issue-5852/horizontal_vertical.rs
new file mode 100644
index 000000000..35e2d0a26
--- /dev/null
+++ b/src/tools/rustfmt/tests/target/issue-5852/horizontal_vertical.rs
@@ -0,0 +1,99 @@
+// rustfmt-imports_layout: HorizontalVertical
+
+use std::{
+ fs,
+ // (temporarily commented, we'll need this again in a second) io,
+};
+
+use foo::{
+ self, // this is important
+};
+
+use foo::bar;
+
+use foo::bar;
+
+use foo::{
+ bar, // abc
+};
+
+use foo::{
+ bar,
+ // abc
+};
+
+use foo::{
+ // 345
+ bar,
+};
+
+use foo::{
+ self, // abc
+};
+
+use foo::{
+ self,
+ // abc
+};
+
+use foo::{
+ // 345
+ self,
+};
+
+use foo::{
+ self, // a
+};
+
+use foo::{self /* a */};
+
+use foo::{self /* a */};
+
+use foo::{
+ // abc
+ abc::{
+ xyz, // 123
+ },
+};
+
+use foo::{
+ abc,
+ // abc
+ bar,
+};
+
+use foo::{
+ // abc
+ abc,
+ bar,
+};
+
+use foo::{
+ abc, // abc
+ bar,
+};
+
+use foo::{
+ abc,
+ // abc
+ bar,
+};
+
+use foo::{
+ self,
+ // abc
+ abc::{
+ xyz, // 123
+ },
+};
+
+use foo::{
+ self,
+ // abc
+ abc::{
+ // 123
+ xyz,
+ },
+};
+
+use path::{self /*comment*/};
diff --git a/src/tools/rustfmt/tests/target/issue-5852/issue_example.rs b/src/tools/rustfmt/tests/target/issue-5852/issue_example.rs
new file mode 100644
index 000000000..463262914
--- /dev/null
+++ b/src/tools/rustfmt/tests/target/issue-5852/issue_example.rs
@@ -0,0 +1,8 @@
+use std::{
+ fs,
+ // (temporarily commented, we'll need this again in a second) io,
+};
+
+use foo::{
+ self, // this is important
+};
diff --git a/src/tools/rustfmt/tests/target/issue-5852/split.rs b/src/tools/rustfmt/tests/target/issue-5852/split.rs
new file mode 100644
index 000000000..e00086dd4
--- /dev/null
+++ b/src/tools/rustfmt/tests/target/issue-5852/split.rs
@@ -0,0 +1,102 @@
+// rustfmt-imports_granularity: Item
+
+use std::{
+ fs,
+ // (temporarily commented, we'll need this again in a second) io,
+};
+
+use foo::{
+ self, // this is important
+};
+
+use foo::bar;
+
+use foo::bar;
+
+use foo::{
+ bar, // abc
+};
+
+use foo::{
+ bar,
+ // abc
+};
+
+use foo::{
+ // 345
+ bar,
+};
+
+use foo::{
+ self, // abc
+};
+
+use foo::{
+ self,
+ // abc
+};
+
+use foo::{
+ // 345
+ self,
+};
+
+use foo::{
+ self, // a
+};
+
+use foo::{self /* a */};
+
+use foo::{self /* a */};
+
+use foo::{
+ // abc
+ abc::{
+ xyz, // 123
+ },
+};
+
+use foo::abc;
+use foo::bar;
+
+use foo::{
+ abc,
+ // abc
+ bar,
+};
+
+use foo::{
+ // abc
+ abc,
+ bar,
+};
+
+use foo::{
+ abc, // abc
+ bar,
+};
+
+use foo::{
+ abc,
+ // abc
+ bar,
+};
+
+use foo::{
+ self,
+ // abc
+ abc::{
+ xyz, // 123
+ },
+};
+
+use foo::{
+ self,
+ // abc
+ abc::{
+ // 123
+ xyz,
+ },
+};
+
+use path::{self /*comment*/};
diff --git a/src/tools/rustfmt/tests/target/issue-5852/vertical.rs b/src/tools/rustfmt/tests/target/issue-5852/vertical.rs
new file mode 100644
index 000000000..f53a68c94
--- /dev/null
+++ b/src/tools/rustfmt/tests/target/issue-5852/vertical.rs
@@ -0,0 +1,105 @@
+// rustfmt-imports_layout: Vertical
+
+use std::{
+ fs,
+ // (temporarily commented, we'll need this again in a second) io,
+};
+
+use foo::{
+ self, // this is important
+};
+
+use foo::bar;
+
+use foo::bar;
+
+use foo::{
+ bar, // abc
+};
+
+use foo::{
+ bar,
+ // abc
+};
+
+use foo::{
+ // 345
+ bar,
+};
+
+use foo::{
+ self, // abc
+};
+
+use foo::{
+ self,
+ // abc
+};
+
+use foo::{
+ // 345
+ self,
+};
+
+use foo::{
+ self, // a
+};
+
+use foo::{
+ self, /* a */
+};
+
+use foo::{
+ self, /* a */
+};
+
+use foo::{
+ // abc
+ abc::{
+ xyz, // 123
+ },
+};
+
+use foo::{
+ abc,
+ // abc
+ bar,
+};
+
+use foo::{
+ // abc
+ abc,
+ bar,
+};
+
+use foo::{
+ abc, // abc
+ bar,
+};
+
+use foo::{
+ abc,
+ // abc
+ bar,
+};
+
+use foo::{
+ self,
+ // abc
+ abc::{
+ xyz, // 123
+ },
+};
+
+use foo::{
+ self,
+ // abc
+ abc::{
+ // 123
+ xyz,
+ },
+};
+
+use path::{
+ self, /*comment*/
+};
diff --git a/src/tools/rustfmt/tests/target/issue-5871.rs b/src/tools/rustfmt/tests/target/issue-5871.rs
new file mode 100644
index 000000000..3116533bc
--- /dev/null
+++ b/src/tools/rustfmt/tests/target/issue-5871.rs
@@ -0,0 +1,8 @@
+#![feature(stmt_expr_attributes)]
+fn okay() -> u32 {
+ (
+ // Comments in parentheses-expressions caused attributes to be duplicated.
+ #[allow(unused_variables)]
+ 0
+ )
+}
diff --git a/src/tools/rustfmt/tests/target/issue-5935.rs b/src/tools/rustfmt/tests/target/issue-5935.rs
new file mode 100644
index 000000000..ebc62c464
--- /dev/null
+++ b/src/tools/rustfmt/tests/target/issue-5935.rs
@@ -0,0 +1,8 @@
+struct Regs<
+ const BEGIN: u64,
+ const END: u64,
+ const DIM: usize,
+ const N: usize = { (END - BEGIN) as usize / (8 * DIM) + 1 },
+> {
+ _foo: u64,
+}
diff --git a/src/tools/rustfmt/tests/target/issue_4110.rs b/src/tools/rustfmt/tests/target/issue_4110.rs
index d3734e90b..ea8fa3b73 100644
--- a/src/tools/rustfmt/tests/target/issue_4110.rs
+++ b/src/tools/rustfmt/tests/target/issue_4110.rs
@@ -12,7 +12,7 @@ fn bindings() {
span,
..
},
- ) if borrow_spans.for_generator() | borrow_spans.for_closure() => self
+ ) if borrow_spans.for_coroutine() | borrow_spans.for_closure() => self
.report_escaping_closure_capture(
borrow_spans,
borrow_span,
diff --git a/src/tools/rustfmt/tests/target/issue_5533.rs b/src/tools/rustfmt/tests/target/issue_5533.rs
new file mode 100644
index 000000000..c3095a440
--- /dev/null
+++ b/src/tools/rustfmt/tests/target/issue_5533.rs
@@ -0,0 +1,6 @@
+// rustfmt-format_code_in_doc_comments: true
+
+struct TestStruct {
+ position_currency: String, // Currency for position of this contract. If not null, 1 contract = 1 positionCurrency.
+ pu: Option<i64>, // Previous event update sequense ("u" of previous message), -1 also means None
+}
diff --git a/src/tools/rustfmt/tests/target/issue_5542.rs b/src/tools/rustfmt/tests/target/issue_5542.rs
new file mode 100644
index 000000000..730bb7b68
--- /dev/null
+++ b/src/tools/rustfmt/tests/target/issue_5542.rs
@@ -0,0 +1,10 @@
+#![feature(dyn_star)]
+#![allow(incomplete_features)]
+
+use core::fmt::Debug;
+
+fn main() {
+ let i = 42;
+ let dyn_i = i as dyn* Debug;
+ dbg!(dyn_i);
+}
diff --git a/src/tools/rustfmt/tests/target/issue_5676.rs b/src/tools/rustfmt/tests/target/issue_5676.rs
new file mode 100644
index 000000000..258771105
--- /dev/null
+++ b/src/tools/rustfmt/tests/target/issue_5676.rs
@@ -0,0 +1,8 @@
+fn main() {
+ match true {
+ true => 'a: {
+ break 'a;
+ }
+ _ => (),
+ }
+}
diff --git a/src/tools/rustfmt/tests/target/issue_5721.rs b/src/tools/rustfmt/tests/target/issue_5721.rs
new file mode 100644
index 000000000..d073b09ca
--- /dev/null
+++ b/src/tools/rustfmt/tests/target/issue_5721.rs
@@ -0,0 +1,10 @@
+#![feature(non_lifetime_binders)]
+#![allow(incomplete_features)]
+
+trait Other<U: ?Sized> {}
+
+trait Trait<U>
+where
+ for<T> U: Other<T>,
+{
+}
diff --git a/src/tools/rustfmt/tests/target/issue_5730.rs b/src/tools/rustfmt/tests/target/issue_5730.rs
new file mode 100644
index 000000000..7922fdcc9
--- /dev/null
+++ b/src/tools/rustfmt/tests/target/issue_5730.rs
@@ -0,0 +1,3 @@
+macro_rules! statement {
+ () => {};
+}
diff --git a/src/tools/rustfmt/tests/target/issue_5735.rs b/src/tools/rustfmt/tests/target/issue_5735.rs
new file mode 100644
index 000000000..2d1376303
--- /dev/null
+++ b/src/tools/rustfmt/tests/target/issue_5735.rs
@@ -0,0 +1,6 @@
+fn find_errors(mut self) {
+ let errors: Vec = vec![
+ #[debug_format = "A({})"]
+ struct A {}
+ ];
+}
diff --git a/src/tools/rustfmt/tests/target/issue_5882.rs b/src/tools/rustfmt/tests/target/issue_5882.rs
new file mode 100644
index 000000000..565fb434a
--- /dev/null
+++ b/src/tools/rustfmt/tests/target/issue_5882.rs
@@ -0,0 +1,7 @@
+macro_rules! foo {}
+macro_rules! bar { /*comment*/ }
+macro_rules! baz { //comment
+}
+macro_rules! foobar {
+ //comment
+}
diff --git a/src/tools/rustfmt/tests/target/issue_5907.rs b/src/tools/rustfmt/tests/target/issue_5907.rs
new file mode 100644
index 000000000..144de636b
--- /dev/null
+++ b/src/tools/rustfmt/tests/target/issue_5907.rs
@@ -0,0 +1,6 @@
+// rustfmt-format_code_in_doc_comments: true
+
+// ```
+// [
+// ]
+// ```
diff --git a/src/tools/rustfmt/tests/target/let_chains.rs b/src/tools/rustfmt/tests/target/let_chains.rs
new file mode 100644
index 000000000..1ceecac8a
--- /dev/null
+++ b/src/tools/rustfmt/tests/target/let_chains.rs
@@ -0,0 +1,129 @@
+fn main() {
+ if let x = x
+ && x
+ {}
+
+ if xxx && let x = x {}
+
+ if aaaaaaaaaaaaaaaaaaaaa
+ && aaaaaaaaaaaaaaa
+ && aaaaaaaaa
+ && let Some(x) = xxxxxxxxxxxx
+ && aaaaaaa
+ && let None = aaaaaaaaaa
+ {}
+
+ if aaaaaaaaaaaaaaaaaaaaa
+ && aaaaaaaaaaaaaaa
+ && aaaaaaaaa
+ && let Some(x) = xxxxxxxxxxxx
+ && aaaaaaa
+ && let None = aaaaaaaaaa
+ {}
+
+ if let Some(Struct { x: TS(1, 2) }) = path::to::<_>(hehe)
+ && let [Simple, people] = /* get ready */
+ create_universe(/* hi */ GreatPowers)
+ .initialize_badminton()
+ .populate_swamps()
+ && let everybody = (Loops {
+ hi, /*hi*/
+ ..loopy()
+ })
+ && summons::triumphantly()
+ {
+ todo!()
+ }
+
+ if let XXXXXXXXX {
+ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,
+ yyyyyyyyyyyyy,
+ zzzzzzzzzzzzz,
+ } = xxxxxxx()
+ && let Foo = bar()
+ {
+ todo!()
+ }
+}
+
+fn test_single_line_let_chain() {
+ // first item in let-chain is an ident
+ if a && let Some(b) = foo() {}
+
+ // first item in let-chain is a unary ! with an ident
+ let unary_not = if !from_hir_call && let Some(p) = parent {};
+
+ // first item in let-chain is a unary * with an ident
+ let unary_deref = if *some_deref && let Some(p) = parent {};
+
+ // first item in let-chain is a unary - (neg) with an ident
+ let unary_neg = if -some_ident && let Some(p) = parent {};
+
+ // first item in let-chain is a try (?) with an ident
+ let try_ = if some_try? && let Some(p) = parent {};
+
+ // first item in let-chain is an ident wrapped in parens
+ let in_parens = if (some_ident) && let Some(p) = parent {};
+
+ // first item in let-chain is a ref & with an ident
+ let _ref = if &some_ref && let Some(p) = parent {};
+
+ // first item in let-chain is a ref &mut with an ident
+ let mut_ref = if &mut some_ref && let Some(p) = parent {};
+
+ // chain unary ref and try
+ let chain_of_unary_ref_and_try = if !&*some_ref? && let Some(p) = parent {};
+}
+
+fn test_multi_line_let_chain() {
+ // Can only single line the let-chain if the first item is an ident
+ if let Some(x) = y
+ && a
+ {}
+
+ // More than one let-chain must be formatted on multiple lines
+ if let Some(x) = y
+ && let Some(a) = b
+ {}
+
+ // The ident isn't long enough so we don't wrap the first let-chain
+ if a && let Some(x) = y
+ && let Some(a) = b
+ {}
+
+ // The ident is long enough so both let-chains are wrapped
+ if aaa
+ && let Some(x) = y
+ && let Some(a) = b
+ {}
+
+ // function call
+ if a()
+ && let Some(x) = y
+ {}
+
+ // bool literal
+ if true
+ && let Some(x) = y
+ {}
+
+ // cast to a bool
+ if 1 as bool
+ && let Some(x) = y
+ {}
+
+ // matches! macro call
+ if matches!(a, some_type)
+ && let Some(x) = y
+ {}
+
+ // block expression returning bool
+ if { true }
+ && let Some(x) = y
+ {}
+
+ // field access
+ if a.x
+ && let Some(x) = y
+ {}
+}
diff --git a/src/tools/rustfmt/tests/target/let_else.rs b/src/tools/rustfmt/tests/target/let_else.rs
index 6554a0961..f6560e854 100644
--- a/src/tools/rustfmt/tests/target/let_else.rs
+++ b/src/tools/rustfmt/tests/target/let_else.rs
@@ -252,3 +252,34 @@ fn with_trailing_try_operator() {
return;
};
}
+
+fn issue5901() {
+ #[cfg(target_os = "linux")]
+ let Some(x) = foo
+ else {
+ todo!()
+ };
+
+ #[cfg(target_os = "linux")]
+ // Some comments between attributes and let-else statement
+ let Some(x) = foo
+ else {
+ todo!()
+ };
+
+ #[cfg(target_os = "linux")]
+ #[cfg(target_arch = "x86_64")]
+ let Some(x) = foo
+ else {
+ todo!()
+ };
+
+ // The else block will be multi-lined because attributes and comments before `let`
+ // are included when calculating max width
+ #[cfg(target_os = "linux")]
+ #[cfg(target_arch = "x86_64")]
+ // Some comments between attributes and let-else statement
+ let Some(x) = foo() else {
+ todo!()
+ };
+}
diff --git a/src/tools/rustfmt/tests/target/let_else_v2.rs b/src/tools/rustfmt/tests/target/let_else_v2.rs
new file mode 100644
index 000000000..b25ac1609
--- /dev/null
+++ b/src/tools/rustfmt/tests/target/let_else_v2.rs
@@ -0,0 +1,73 @@
+// rustfmt-version: Two
+// rustfmt-single_line_let_else_max_width: 100
+
+fn issue5901() {
+ #[cfg(target_os = "linux")]
+ let Some(x) = foo else { todo!() };
+
+ #[cfg(target_os = "linux")]
+ // Some comments between attributes and let-else statement
+ let Some(x) = foo else { todo!() };
+
+ #[cfg(target_os = "linux")]
+ #[cfg(target_arch = "x86_64")]
+ let Some(x) = foo else { todo!() };
+
+ // The else block is multi-lined
+ #[cfg(target_os = "linux")]
+ let Some(x) = foo else {
+ return;
+ };
+
+ // The else block will be single-lined because attributes and comments before `let`
+ // are no longer included when calculating max width
+ #[cfg(target_os = "linux")]
+ #[cfg(target_arch = "x86_64")]
+ // Some comments between attributes and let-else statement
+ let Some(x) = foo else { todo!() };
+
+ // Some more test cases for v2 formatting with attributes
+
+ #[cfg(target_os = "linux")]
+ #[cfg(target_arch = "x86_64")]
+ let Some(x) = opt
+ // pre else keyword line-comment
+ else {
+ return;
+ };
+
+ #[cfg(target_os = "linux")]
+ #[cfg(target_arch = "x86_64")]
+ let Some(x) = opt else
+ // post else keyword line-comment
+ {
+ return;
+ };
+
+ #[cfg(target_os = "linux")]
+ #[cfg(target_arch = "x86_64")]
+ let Foo {
+ x: Bar(..),
+ y: FooBar(..),
+ z: Baz(..),
+ } = opt
+ else {
+ return;
+ };
+
+ #[cfg(target_os = "linux")]
+ #[cfg(target_arch = "x86_64")]
+ let Some(Ok((Message::ChangeColor(super::color::Color::Rgb(r, g, b)), Point { x, y, z }))) =
+ opt
+ else {
+ return;
+ };
+
+ #[cfg(target_os = "linux")]
+ #[cfg(target_arch = "x86_64")]
+ let Some(x) =
+ very_very_very_very_very_very_very_very_very_very_very_very_long_expression_in_assign_rhs()
+ else {
+ return;
+ };
+}
diff --git a/src/tools/rustfmt/tests/target/match.rs b/src/tools/rustfmt/tests/target/match.rs
index 1bf3fb758..0e7815a81 100644
--- a/src/tools/rustfmt/tests/target/match.rs
+++ b/src/tools/rustfmt/tests/target/match.rs
@@ -317,6 +317,14 @@ fn guards() {
if fooooooooooooooooooooo
&& (bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
|| cccccccccccccccccccccccccccccccccccccccc) => {}
+ Hi { friend } if let None = friend => {}
+ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+ if let Some(foooooooooooooo) = hiiiiiiiiiiiiiii => {}
+ aaaaaaaaaaaaaaaaa
+ if let Superman {
+ powers: Some(goteem),
+ ..
+ } = all::get_random_being::<Super>() => {}
}
}
diff --git a/src/tools/rustfmt/tests/target/non-lifetime-binders.rs b/src/tools/rustfmt/tests/target/non-lifetime-binders.rs
new file mode 100644
index 000000000..ca6941a0c
--- /dev/null
+++ b/src/tools/rustfmt/tests/target/non-lifetime-binders.rs
@@ -0,0 +1,10 @@
+fn main()
+where
+ for<'a, T: Sized + 'a, const C: usize> [&'a T; C]: Sized,
+{
+ let x = for<T> || {};
+
+ let y: dyn for<T> Into<T>;
+
+ let z: for<T> fn(T);
+}
diff --git a/src/tools/rustfmt/tests/target/skip_macro_invocations/config_file.rs b/src/tools/rustfmt/tests/target/skip_macro_invocations/config_file.rs
new file mode 100644
index 000000000..008e28db4
--- /dev/null
+++ b/src/tools/rustfmt/tests/target/skip_macro_invocations/config_file.rs
@@ -0,0 +1,7 @@
+// rustfmt-unstable: true
+// rustfmt-config: issue-5816.toml
+
+fn main() {
+ println!( "Hello, world!");
+ let x = 7;
+}
diff --git a/src/tools/rustfmt/triagebot.toml b/src/tools/rustfmt/triagebot.toml
index fa0824ac5..b8691192e 100644
--- a/src/tools/rustfmt/triagebot.toml
+++ b/src/tools/rustfmt/triagebot.toml
@@ -1 +1,4 @@
+[autolabel."pr-not-reviewed"]
+new_pr = true
+
[assign]
diff --git a/src/tools/suggest-tests/src/main.rs b/src/tools/suggest-tests/src/main.rs
index 0b541b60c..8e3625c24 100644
--- a/src/tools/suggest-tests/src/main.rs
+++ b/src/tools/suggest-tests/src/main.rs
@@ -1,10 +1,17 @@
use std::process::ExitCode;
-use build_helper::git::get_git_modified_files;
+use build_helper::git::{get_git_modified_files, GitConfig};
use suggest_tests::get_suggestions;
fn main() -> ExitCode {
- let modified_files = get_git_modified_files(None, &Vec::new());
+ let modified_files = get_git_modified_files(
+ &GitConfig {
+ git_repository: &env("SUGGEST_TESTS_GIT_REPOSITORY"),
+ nightly_branch: &env("SUGGEST_TESTS_NIGHTLY_BRANCH"),
+ },
+ None,
+ &Vec::new(),
+ );
let modified_files = match modified_files {
Ok(Some(files)) => files,
Ok(None) => {
@@ -25,3 +32,13 @@ fn main() -> ExitCode {
ExitCode::SUCCESS
}
+
+fn env(key: &str) -> String {
+ match std::env::var(key) {
+ Ok(var) => var,
+ Err(err) => {
+ eprintln!("suggest-tests: failed to read environment variable {key}: {err}");
+ std::process::exit(1);
+ }
+ }
+}
diff --git a/src/tools/tidy/src/alphabetical.rs b/src/tools/tidy/src/alphabetical.rs
index fdc411c89..150a95943 100644
--- a/src/tools/tidy/src/alphabetical.rs
+++ b/src/tools/tidy/src/alphabetical.rs
@@ -1,6 +1,6 @@
//! Checks that a list of items is in alphabetical order
//!
-//! To use, use the following annotation in the code:
+//! Use the following marker in the code:
//! ```rust
//! // tidy-alphabetical-start
//! fn aaa() {}
@@ -10,17 +10,23 @@
//! ```
//!
//! The following lines are ignored:
+//! - Empty lines
//! - Lines that are indented with more or less spaces than the first line
-//! - Lines starting with `//`, `#[`, `)`, `]`, `}` if the comment has the same indentation as
-//! the first line
+//! - Lines starting with `//`, `#` (except those starting with `#!`), `)`, `]`, `}` if the comment
+//! has the same indentation as the first line
+//! - Lines starting with a closing delimiter (`)`, `[`, `}`) are ignored.
//!
-//! If a line ends with an opening bracket, the line is ignored and the next line will have
-//! its extra indentation ignored.
+//! If a line ends with an opening delimiter, we effectively join the following line to it before
+//! checking it. E.g. `foo(\nbar)` is treated like `foo(bar)`.
-use std::{fmt::Display, path::Path};
+use std::fmt::Display;
+use std::path::Path;
use crate::walk::{filter_dirs, walk};
+#[cfg(test)]
+mod tests;
+
fn indentation(line: &str) -> usize {
line.find(|c| c != ' ').unwrap_or(0)
}
@@ -29,28 +35,36 @@ fn is_close_bracket(c: char) -> bool {
matches!(c, ')' | ']' | '}')
}
-// Don't let tidy check this here :D
-const START_COMMENT: &str = concat!("// tidy-alphabetical", "-start");
-const END_COMMENT: &str = "// tidy-alphabetical-end";
+const START_MARKER: &str = "tidy-alphabetical-start";
+const END_MARKER: &str = "tidy-alphabetical-end";
fn check_section<'a>(
file: impl Display,
lines: impl Iterator<Item = (usize, &'a str)>,
+ err: &mut dyn FnMut(&str) -> std::io::Result<()>,
bad: &mut bool,
) {
- let content_lines = lines.take_while(|(_, line)| !line.contains(END_COMMENT));
-
let mut prev_line = String::new();
let mut first_indent = None;
let mut in_split_line = None;
- for (line_idx, line) in content_lines {
- if line.contains(START_COMMENT) {
- tidy_error!(
+ for (idx, line) in lines {
+ if line.is_empty() {
+ continue;
+ }
+
+ if line.contains(START_MARKER) {
+ tidy_error_ext!(
+ err,
bad,
- "{file}:{} found `{START_COMMENT}` expecting `{END_COMMENT}`",
- line_idx
- )
+ "{file}:{} found `{START_MARKER}` expecting `{END_MARKER}`",
+ idx + 1
+ );
+ return;
+ }
+
+ if line.contains(END_MARKER) {
+ return;
}
let indent = first_indent.unwrap_or_else(|| {
@@ -60,6 +74,7 @@ fn check_section<'a>(
});
let line = if let Some(prev_split_line) = in_split_line {
+ // Join the split lines.
in_split_line = None;
format!("{prev_split_line}{}", line.trim_start())
} else {
@@ -73,7 +88,7 @@ fn check_section<'a>(
let trimmed_line = line.trim_start_matches(' ');
if trimmed_line.starts_with("//")
- || trimmed_line.starts_with("#[")
+ || (trimmed_line.starts_with("#") && !trimmed_line.starts_with("#!"))
|| trimmed_line.starts_with(is_close_bracket)
{
continue;
@@ -87,25 +102,44 @@ fn check_section<'a>(
let prev_line_trimmed_lowercase = prev_line.trim_start_matches(' ').to_lowercase();
if trimmed_line.to_lowercase() < prev_line_trimmed_lowercase {
- tidy_error!(bad, "{file}:{}: line not in alphabetical order", line_idx + 1,);
+ tidy_error_ext!(err, bad, "{file}:{}: line not in alphabetical order", idx + 1);
}
prev_line = line;
}
+
+ tidy_error_ext!(err, bad, "{file}: reached end of file expecting `{END_MARKER}`")
}
-pub fn check(path: &Path, bad: &mut bool) {
- walk(path, |path, _is_dir| filter_dirs(path), &mut |entry, contents| {
- let file = &entry.path().display();
+fn check_lines<'a>(
+ file: &impl Display,
+ mut lines: impl Iterator<Item = (usize, &'a str)>,
+ err: &mut dyn FnMut(&str) -> std::io::Result<()>,
+ bad: &mut bool,
+) {
+ while let Some((idx, line)) = lines.next() {
+ if line.contains(END_MARKER) {
+ tidy_error_ext!(
+ err,
+ bad,
+ "{file}:{} found `{END_MARKER}` expecting `{START_MARKER}`",
+ idx + 1
+ )
+ }
- let mut lines = contents.lines().enumerate().peekable();
- while let Some((_, line)) = lines.next() {
- if line.contains(START_COMMENT) {
- check_section(file, &mut lines, bad);
- if lines.peek().is_none() {
- tidy_error!(bad, "{file}: reached end of file expecting `{END_COMMENT}`")
- }
- }
+ if line.contains(START_MARKER) {
+ check_section(file, &mut lines, err, bad);
}
+ }
+}
+
+pub fn check(path: &Path, bad: &mut bool) {
+ let skip =
+ |path: &_, _is_dir| filter_dirs(path) || path.ends_with("tidy/src/alphabetical/tests.rs");
+
+ walk(path, skip, &mut |entry, contents| {
+ let file = &entry.path().display();
+ let lines = contents.lines().enumerate();
+ check_lines(file, lines, &mut crate::tidy_error, bad)
});
}
diff --git a/src/tools/tidy/src/alphabetical/tests.rs b/src/tools/tidy/src/alphabetical/tests.rs
new file mode 100644
index 000000000..560e0284b
--- /dev/null
+++ b/src/tools/tidy/src/alphabetical/tests.rs
@@ -0,0 +1,188 @@
+use super::*;
+use std::io::Write;
+use std::str::from_utf8;
+
+fn test(lines: &str, name: &str, expected_msg: &str, expected_bad: bool) {
+ let mut actual_msg = Vec::new();
+ let mut actual_bad = false;
+ let mut err = |args: &_| {
+ write!(&mut actual_msg, "{args}")?;
+ Ok(())
+ };
+ check_lines(&name, lines.lines().enumerate(), &mut err, &mut actual_bad);
+ assert_eq!(expected_msg, from_utf8(&actual_msg).unwrap());
+ assert_eq!(expected_bad, actual_bad);
+}
+
+fn good(lines: &str) {
+ test(lines, "good", "", false);
+}
+
+fn bad(lines: &str, expected_msg: &str) {
+ test(lines, "bad", expected_msg, true);
+}
+
+#[test]
+fn test_no_markers() {
+ let lines = "\
+ def
+ abc
+ xyz
+ ";
+ good(lines);
+}
+
+#[test]
+fn test_rust_good() {
+ let lines = "\
+ // tidy-alphabetical-start
+ abc
+ def
+ xyz
+ // tidy-alphabetical-end"; // important: end marker on last line
+ good(lines);
+}
+
+#[test]
+fn test_complex_good() {
+ let lines = "\
+ zzz
+
+ // tidy-alphabetical-start
+ abc
+ // Rust comments are ok
+ def
+ # TOML comments are ok
+ xyz
+ // tidy-alphabetical-end
+
+ # tidy-alphabetical-start
+ foo(abc);
+ // blank lines are ok
+
+ // split line gets joined
+ foo(
+ def
+ );
+
+ foo(xyz);
+ # tidy-alphabetical-end
+
+ % tidy-alphabetical-start
+ abc
+ ignored_due_to_different_indent
+ def
+ % tidy-alphabetical-end
+
+ aaa
+ ";
+ good(lines);
+}
+
+#[test]
+fn test_rust_bad() {
+ let lines = "\
+ // tidy-alphabetical-start
+ abc
+ xyz
+ def
+ // tidy-alphabetical-end
+ ";
+ bad(lines, "bad:4: line not in alphabetical order");
+}
+
+#[test]
+fn test_toml_bad() {
+ let lines = "\
+ # tidy-alphabetical-start
+ abc
+ xyz
+ def
+ # tidy-alphabetical-end
+ ";
+ bad(lines, "bad:4: line not in alphabetical order");
+}
+
+#[test]
+fn test_features_bad() {
+ // Even though lines starting with `#` are treated as comments, lines
+ // starting with `#!` are an exception.
+ let lines = "\
+ tidy-alphabetical-start
+ #![feature(abc)]
+ #![feature(xyz)]
+ #![feature(def)]
+ tidy-alphabetical-end
+ ";
+ bad(lines, "bad:4: line not in alphabetical order");
+}
+
+#[test]
+fn test_indent_bad() {
+ // All lines are indented the same amount, and so are checked.
+ let lines = "\
+ $ tidy-alphabetical-start
+ abc
+ xyz
+ def
+ $ tidy-alphabetical-end
+ ";
+ bad(lines, "bad:4: line not in alphabetical order");
+}
+
+#[test]
+fn test_split_bad() {
+ let lines = "\
+ || tidy-alphabetical-start
+ foo(abc)
+ foo(
+ xyz
+ )
+ foo(
+ def
+ )
+ && tidy-alphabetical-end
+ ";
+ bad(lines, "bad:7: line not in alphabetical order");
+}
+
+#[test]
+fn test_double_start() {
+ let lines = "\
+ tidy-alphabetical-start
+ abc
+ tidy-alphabetical-start
+ ";
+ bad(lines, "bad:3 found `tidy-alphabetical-start` expecting `tidy-alphabetical-end`");
+}
+
+#[test]
+fn test_missing_start() {
+ let lines = "\
+ abc
+ tidy-alphabetical-end
+ abc
+ ";
+ bad(lines, "bad:2 found `tidy-alphabetical-end` expecting `tidy-alphabetical-start`");
+}
+
+#[test]
+fn test_missing_end() {
+ let lines = "\
+ tidy-alphabetical-start
+ abc
+ ";
+ bad(lines, "bad: reached end of file expecting `tidy-alphabetical-end`");
+}
+
+#[test]
+fn test_double_end() {
+ let lines = "\
+ tidy-alphabetical-start
+ abc
+ tidy-alphabetical-end
+ def
+ tidy-alphabetical-end
+ ";
+ bad(lines, "bad:5 found `tidy-alphabetical-end` expecting `tidy-alphabetical-start`");
+}
diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs
index 7d3ef4197..f88f91655 100644
--- a/src/tools/tidy/src/deps.rs
+++ b/src/tools/tidy/src/deps.rs
@@ -31,12 +31,49 @@ const LICENSES: &[&str] = &[
// tidy-alphabetical-end
];
+type ExceptionList = &'static [(&'static str, &'static str)];
+
+/// The workspaces to check for licensing and optionally permitted dependencies.
+///
+/// Each entry consists of a tuple with the following elements:
+///
+/// * The path to the workspace root Cargo.toml file.
+/// * The list of license exceptions.
+/// * Optionally a tuple of:
+/// * A list of crates for which dependencies need to be explicitly allowed.
+/// * The list of allowed dependencies.
+// FIXME auto detect all cargo workspaces
+pub(crate) const WORKSPACES: &[(&str, ExceptionList, Option<(&[&str], &[&str])>)] = &[
+ // The root workspace has to be first for check_rustfix to work.
+ (".", EXCEPTIONS, Some((&["rustc-main"], PERMITTED_RUSTC_DEPENDENCIES))),
+ // Outside of the alphabetical section because rustfmt formats it using multiple lines.
+ (
+ "compiler/rustc_codegen_cranelift",
+ EXCEPTIONS_CRANELIFT,
+ Some((&["rustc_codegen_cranelift"], PERMITTED_CRANELIFT_DEPENDENCIES)),
+ ),
+ // tidy-alphabetical-start
+ //("compiler/rustc_codegen_gcc", EXCEPTIONS_GCC, None), // FIXME uncomment once all deps are vendored
+ //("library/backtrace", &[], None), // FIXME uncomment once rust-lang/backtrace#562 has been synced back to the rust repo
+ //("library/portable-simd", &[], None), // FIXME uncomment once rust-lang/portable-simd#363 has been synced back to the rust repo
+ //("library/stdarch", EXCEPTIONS_STDARCH, None), // FIXME uncomment once rust-lang/stdarch#1462 has been synced back to the rust repo
+ ("src/bootstrap", EXCEPTIONS_BOOTSTRAP, None),
+ ("src/ci/docker/host-x86_64/test-various/uefi_qemu_test", EXCEPTIONS_UEFI_QEMU_TEST, None),
+ //("src/etc/test-float-parse", &[], None), // FIXME uncomment once all deps are vendored
+ ("src/tools/cargo", EXCEPTIONS_CARGO, None),
+ //("src/tools/miri/test-cargo-miri", &[], None), // FIXME uncomment once all deps are vendored
+ //("src/tools/miri/test_dependencies", &[], None), // FIXME uncomment once all deps are vendored
+ ("src/tools/rust-analyzer", EXCEPTIONS_RUST_ANALYZER, None),
+ ("src/tools/x", &[], None),
+ // tidy-alphabetical-end
+];
+
/// These are exceptions to Rust's permissive licensing policy, and
/// should be considered bugs. Exceptions are only allowed in Rust
/// tooling. It is _crucial_ that no exception crates be dependencies
/// of the Rust runtime (std/test).
#[rustfmt::skip]
-const EXCEPTIONS: &[(&str, &str)] = &[
+const EXCEPTIONS: ExceptionList = &[
// tidy-alphabetical-start
("ar_archive_writer", "Apache-2.0 WITH LLVM-exception"), // rustc
("colored", "MPL-2.0"), // rustfmt
@@ -47,18 +84,29 @@ const EXCEPTIONS: &[(&str, &str)] = &[
("instant", "BSD-3-Clause"), // rustc_driver/tracing-subscriber/parking_lot
("mdbook", "MPL-2.0"), // mdbook
("openssl", "Apache-2.0"), // opt-dist
+ ("option-ext", "MPL-2.0"), // cargo-miri (via `directories`)
("rustc_apfloat", "Apache-2.0 WITH LLVM-exception"), // rustc (license is the same as LLVM uses)
- ("ryu", "Apache-2.0 OR BSL-1.0"), // cargo/... (because of serde)
+ ("ryu", "Apache-2.0 OR BSL-1.0"), // BSL is not acceptble, but we use it under Apache-2.0 // cargo/... (because of serde)
("self_cell", "Apache-2.0"), // rustc (fluent translations)
("snap", "BSD-3-Clause"), // rustc
// tidy-alphabetical-end
];
-const EXCEPTIONS_CARGO: &[(&str, &str)] = &[
+// FIXME uncomment once rust-lang/stdarch#1462 lands
+/*
+const EXCEPTIONS_STDARCH: ExceptionList = &[
+ // tidy-alphabetical-start
+ ("ryu", "Apache-2.0 OR BSL-1.0"), // BSL is not acceptble, but we use it under Apache-2.0
+ ("wasmparser", "Apache-2.0 WITH LLVM-exception"),
+ ("wasmprinter", "Apache-2.0 WITH LLVM-exception"),
+ // tidy-alphabetical-end
+];
+*/
+
+const EXCEPTIONS_CARGO: ExceptionList = &[
// tidy-alphabetical-start
("bitmaps", "MPL-2.0+"),
("bytesize", "Apache-2.0"),
- ("byteyarn", "Apache-2.0"),
("ciborium", "Apache-2.0"),
("ciborium-io", "Apache-2.0"),
("ciborium-ll", "Apache-2.0"),
@@ -68,16 +116,30 @@ const EXCEPTIONS_CARGO: &[(&str, &str)] = &[
("im-rc", "MPL-2.0+"),
("normalize-line-endings", "Apache-2.0"),
("openssl", "Apache-2.0"),
- ("ryu", "Apache-2.0 OR BSL-1.0"),
+ ("ryu", "Apache-2.0 OR BSL-1.0"), // BSL is not acceptble, but we use it under Apache-2.0
("sha1_smol", "BSD-3-Clause"),
("similar", "Apache-2.0"),
("sized-chunks", "MPL-2.0+"),
("subtle", "BSD-3-Clause"),
+ ("supports-hyperlinks", "Apache-2.0"),
("unicode-bom", "Apache-2.0"),
// tidy-alphabetical-end
];
-const EXCEPTIONS_CRANELIFT: &[(&str, &str)] = &[
+const EXCEPTIONS_RUST_ANALYZER: ExceptionList = &[
+ // tidy-alphabetical-start
+ ("anymap", "BlueOak-1.0.0 OR MIT OR Apache-2.0"), // BlueOak is not acceptable, but we use it under MIT OR Apache-2 .0
+ ("dissimilar", "Apache-2.0"),
+ ("instant", "BSD-3-Clause"),
+ ("notify", "CC0-1.0"),
+ ("pulldown-cmark-to-cmark", "Apache-2.0"),
+ ("ryu", "Apache-2.0 OR BSL-1.0"), // BSL is not acceptble, but we use it under Apache-2.0
+ ("scip", "Apache-2.0"),
+ ("snap", "BSD-3-Clause"),
+ // tidy-alphabetical-end
+];
+
+const EXCEPTIONS_CRANELIFT: ExceptionList = &[
// tidy-alphabetical-start
("cranelift-bforest", "Apache-2.0 WITH LLVM-exception"),
("cranelift-codegen", "Apache-2.0 WITH LLVM-exception"),
@@ -98,8 +160,22 @@ const EXCEPTIONS_CRANELIFT: &[(&str, &str)] = &[
// tidy-alphabetical-end
];
-const EXCEPTIONS_BOOTSTRAP: &[(&str, &str)] = &[
- ("ryu", "Apache-2.0 OR BSL-1.0"), // through serde
+// FIXME uncomment once all deps are vendored
+/*
+const EXCEPTIONS_GCC: ExceptionList = &[
+ // tidy-alphabetical-start
+ ("gccjit", "GPL-3.0"),
+ ("gccjit_sys", "GPL-3.0"),
+ // tidy-alphabetical-end
+];
+*/
+
+const EXCEPTIONS_BOOTSTRAP: ExceptionList = &[
+ ("ryu", "Apache-2.0 OR BSL-1.0"), // through serde. BSL is not acceptble, but we use it under Apache-2.0
+];
+
+const EXCEPTIONS_UEFI_QEMU_TEST: ExceptionList = &[
+ ("r-efi", "MIT OR Apache-2.0 OR LGPL-2.1-or-later"), // LGPL is not acceptible, but we use it under MIT OR Apache-2.0
];
/// These are the root crates that are part of the runtime. The licenses for
@@ -141,6 +217,7 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[
"darling_core",
"darling_macro",
"datafrog",
+ "derivative",
"derive_more",
"derive_setters",
"digest",
@@ -152,7 +229,6 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[
"ena",
"equivalent",
"errno",
- "errno-dragonfly",
"expect-test",
"fallible-iterator", // dependency of `thorin`
"fastrand",
@@ -171,7 +247,10 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[
"hashbrown",
"hermit-abi",
"icu_list",
+ "icu_list_data",
"icu_locid",
+ "icu_locid_transform",
+ "icu_locid_transform_data",
"icu_provider",
"icu_provider_adapters",
"icu_provider_macros",
@@ -183,6 +262,7 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[
"is-terminal",
"itertools",
"itoa",
+ "jemalloc-sys",
"jobserver",
"lazy_static",
"libc",
@@ -210,6 +290,7 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[
"perf-event-open-sys",
"pin-project-lite",
"polonius-engine",
+ "portable-atomic", // dependency for platforms doesn't support `AtomicU64` in std
"ppv-lite86",
"proc-macro-hack",
"proc-macro2",
@@ -320,12 +401,37 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[
// tidy-alphabetical-end
];
+// These crates come from ICU4X and are licensed under the unicode license.
+// It currently doesn't have an SPDX identifier, so they cannot put one there.
+// See https://github.com/unicode-org/icu4x/pull/3875
+// FIXME: This should be removed once ICU4X crates update.
+const ICU4X_UNICODE_LICENSE_DEPENDENCIES: &[&str] = &[
+ // tidy-alphabetical-start
+ "icu_list",
+ "icu_list_data",
+ "icu_locid",
+ "icu_locid_transform",
+ "icu_locid_transform_data",
+ "icu_provider",
+ "icu_provider_adapters",
+ "icu_provider_macros",
+ "litemap",
+ "tinystr",
+ "writeable",
+ "yoke",
+ "yoke-derive",
+ "zerofrom",
+ "zerofrom-derive",
+ "zerovec",
+ "zerovec-derive",
+ // tidy-alphabetical-end
+];
+
const PERMITTED_CRANELIFT_DEPENDENCIES: &[&str] = &[
// tidy-alphabetical-start
"ahash",
"anyhow",
"arbitrary",
- "autocfg",
"bitflags",
"bumpalo",
"cfg-if",
@@ -382,65 +488,90 @@ const PERMITTED_CRANELIFT_DEPENDENCIES: &[&str] = &[
/// `root` is path to the directory with the root `Cargo.toml` (for the workspace). `cargo` is path
/// to the cargo executable.
pub fn check(root: &Path, cargo: &Path, bad: &mut bool) {
- let mut cmd = cargo_metadata::MetadataCommand::new();
- cmd.cargo_path(cargo)
- .manifest_path(root.join("Cargo.toml"))
- .features(cargo_metadata::CargoOpt::AllFeatures);
- let metadata = t!(cmd.exec());
- let runtime_ids = compute_runtime_crates(&metadata);
- check_license_exceptions(&metadata, EXCEPTIONS, runtime_ids, bad);
- check_permitted_dependencies(
- &metadata,
- "rustc",
- PERMITTED_RUSTC_DEPENDENCIES,
- &["rustc_driver", "rustc_codegen_llvm"],
- bad,
- );
-
- // Check cargo independently as it has it's own workspace.
- let mut cmd = cargo_metadata::MetadataCommand::new();
- cmd.cargo_path(cargo)
- .manifest_path(root.join("src/tools/cargo/Cargo.toml"))
- .features(cargo_metadata::CargoOpt::AllFeatures);
- let cargo_metadata = t!(cmd.exec());
- let runtime_ids = HashSet::new();
- check_license_exceptions(&cargo_metadata, EXCEPTIONS_CARGO, runtime_ids, bad);
- check_rustfix(&metadata, &cargo_metadata, bad);
-
- // Check rustc_codegen_cranelift independently as it has it's own workspace.
- let mut cmd = cargo_metadata::MetadataCommand::new();
- cmd.cargo_path(cargo)
- .manifest_path(root.join("compiler/rustc_codegen_cranelift/Cargo.toml"))
- .features(cargo_metadata::CargoOpt::AllFeatures);
- let metadata = t!(cmd.exec());
- let runtime_ids = HashSet::new();
- check_license_exceptions(&metadata, EXCEPTIONS_CRANELIFT, runtime_ids, bad);
- check_permitted_dependencies(
- &metadata,
- "cranelift",
- PERMITTED_CRANELIFT_DEPENDENCIES,
- &["rustc_codegen_cranelift"],
- bad,
- );
-
- let mut cmd = cargo_metadata::MetadataCommand::new();
- cmd.cargo_path(cargo)
- .manifest_path(root.join("src/bootstrap/Cargo.toml"))
- .features(cargo_metadata::CargoOpt::AllFeatures);
- let metadata = t!(cmd.exec());
- let runtime_ids = HashSet::new();
- check_license_exceptions(&metadata, EXCEPTIONS_BOOTSTRAP, runtime_ids, bad);
+ let mut checked_runtime_licenses = false;
+ let mut rust_metadata = None;
+
+ for &(workspace, exceptions, permitted_deps) in WORKSPACES {
+ if !root.join(workspace).join("Cargo.lock").exists() {
+ tidy_error!(bad, "the `{workspace}` workspace doesn't have a Cargo.lock");
+ continue;
+ }
+
+ let mut cmd = cargo_metadata::MetadataCommand::new();
+ cmd.cargo_path(cargo)
+ .manifest_path(root.join(workspace).join("Cargo.toml"))
+ .features(cargo_metadata::CargoOpt::AllFeatures)
+ .other_options(vec!["--locked".to_owned()]);
+ let metadata = t!(cmd.exec());
+
+ check_license_exceptions(&metadata, exceptions, bad);
+ if let Some((crates, permitted_deps)) = permitted_deps {
+ check_permitted_dependencies(&metadata, workspace, permitted_deps, crates, bad);
+ }
+
+ if workspace == "." {
+ let runtime_ids = compute_runtime_crates(&metadata);
+ check_runtime_license_exceptions(&metadata, runtime_ids, bad);
+ checked_runtime_licenses = true;
+ rust_metadata = Some(metadata);
+ } else if workspace == "src/tools/cargo" {
+ check_rustfix(
+ rust_metadata
+ .as_ref()
+ .expect("The root workspace should be the first to be checked"),
+ &metadata,
+ bad,
+ );
+ }
+ }
+
+ // Sanity check to ensure we don't accidentally remove the workspace containing the runtime
+ // crates.
+ assert!(checked_runtime_licenses);
}
-/// Check that all licenses are in the valid list in `LICENSES`.
+/// Check that all licenses of runtime dependencies are in the valid list in `LICENSES`.
///
-/// Packages listed in `exceptions` are allowed for tools.
-fn check_license_exceptions(
+/// Unlike for tools we don't allow exceptions to the `LICENSES` list for the runtime with the sole
+/// exception of `fortanix-sgx-abi` which is only used on x86_64-fortanix-unknown-sgx.
+fn check_runtime_license_exceptions(
metadata: &Metadata,
- exceptions: &[(&str, &str)],
runtime_ids: HashSet<&PackageId>,
bad: &mut bool,
) {
+ for pkg in &metadata.packages {
+ if !runtime_ids.contains(&pkg.id) {
+ // Only checking dependencies of runtime libraries here.
+ continue;
+ }
+ if pkg.source.is_none() {
+ // No need to check local packages.
+ continue;
+ }
+ let license = match &pkg.license {
+ Some(license) => license,
+ None => {
+ tidy_error!(bad, "dependency `{}` does not define a license expression", pkg.id);
+ continue;
+ }
+ };
+ if !LICENSES.contains(&license.as_str()) {
+ // This is a specific exception because SGX is considered "third party".
+ // See https://github.com/rust-lang/rust/issues/62620 for more.
+ // In general, these should never be added and this exception
+ // should not be taken as precedent for any new target.
+ if pkg.name == "fortanix-sgx-abi" && pkg.license.as_deref() == Some("MPL-2.0") {
+ continue;
+ }
+ tidy_error!(bad, "invalid license `{}` in `{}`", license, pkg.id);
+ }
+ }
+}
+
+/// Check that all licenses of tool dependencies are in the valid list in `LICENSES`.
+///
+/// Packages listed in `exceptions` are allowed for tools.
+fn check_license_exceptions(metadata: &Metadata, exceptions: &[(&str, &str)], bad: &mut bool) {
// Validate the EXCEPTIONS list hasn't changed.
for (name, license) in exceptions {
// Check that the package actually exists.
@@ -482,24 +613,21 @@ fn check_license_exceptions(
// No need to check local packages.
continue;
}
- if !runtime_ids.contains(&pkg.id) && exception_names.contains(&pkg.name.as_str()) {
+ if exception_names.contains(&pkg.name.as_str()) {
continue;
}
let license = match &pkg.license {
Some(license) => license,
None => {
+ if ICU4X_UNICODE_LICENSE_DEPENDENCIES.contains(&pkg.name.as_str()) {
+ // See the comment on ICU4X_UNICODE_LICENSE_DEPENDENCIES.
+ continue;
+ }
tidy_error!(bad, "dependency `{}` does not define a license expression", pkg.id);
continue;
}
};
if !LICENSES.contains(&license.as_str()) {
- if pkg.name == "fortanix-sgx-abi" {
- // This is a specific exception because SGX is considered
- // "third party". See
- // https://github.com/rust-lang/rust/issues/62620 for more. In
- // general, these should never be added.
- continue;
- }
tidy_error!(bad, "invalid license `{}` in `{}`", license, pkg.id);
}
}
diff --git a/src/tools/tidy/src/extdeps.rs b/src/tools/tidy/src/extdeps.rs
index aad57cacb..ff71ca537 100644
--- a/src/tools/tidy/src/extdeps.rs
+++ b/src/tools/tidy/src/extdeps.rs
@@ -9,25 +9,33 @@ const ALLOWED_SOURCES: &[&str] = &["\"registry+https://github.com/rust-lang/crat
/// Checks for external package sources. `root` is the path to the directory that contains the
/// workspace `Cargo.toml`.
pub fn check(root: &Path, bad: &mut bool) {
- // `Cargo.lock` of rust.
- let path = root.join("Cargo.lock");
+ for &(workspace, _, _) in crate::deps::WORKSPACES {
+ // FIXME check other workspaces too
+ // `Cargo.lock` of rust.
+ let path = root.join(workspace).join("Cargo.lock");
- // Open and read the whole file.
- let cargo_lock = t!(fs::read_to_string(&path));
-
- // Process each line.
- for line in cargo_lock.lines() {
- // Consider only source entries.
- if !line.starts_with("source = ") {
+ if !path.exists() {
+ tidy_error!(bad, "the `{workspace}` workspace doesn't have a Cargo.lock");
continue;
}
- // Extract source value.
- let source = line.split_once('=').unwrap().1.trim();
+ // Open and read the whole file.
+ let cargo_lock = t!(fs::read_to_string(&path));
+
+ // Process each line.
+ for line in cargo_lock.lines() {
+ // Consider only source entries.
+ if !line.starts_with("source = ") {
+ continue;
+ }
+
+ // Extract source value.
+ let source = line.split_once('=').unwrap().1.trim();
- // Ensure source is allowed.
- if !ALLOWED_SOURCES.contains(&&*source) {
- tidy_error!(bad, "invalid source: {}", source);
+ // Ensure source is allowed.
+ if !ALLOWED_SOURCES.contains(&&*source) {
+ tidy_error!(bad, "invalid source: {}", source);
+ }
}
}
}
diff --git a/src/tools/tidy/src/features.rs b/src/tools/tidy/src/features.rs
index d900c04c1..8e791a7dc 100644
--- a/src/tools/tidy/src/features.rs
+++ b/src/tools/tidy/src/features.rs
@@ -30,7 +30,7 @@ const FEATURE_GROUP_END_PREFIX: &str = "// feature-group-end";
#[derive(Debug, PartialEq, Clone)]
pub enum Status {
- Stable,
+ Accepted,
Removed,
Unstable,
}
@@ -38,7 +38,7 @@ pub enum Status {
impl fmt::Display for Status {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let as_str = match *self {
- Status::Stable => "stable",
+ Status::Accepted => "accepted",
Status::Unstable => "unstable",
Status::Removed => "removed",
};
@@ -279,9 +279,9 @@ fn test_filen_gate(filen_underscore: &str, features: &mut Features) -> bool {
pub fn collect_lang_features(base_compiler_path: &Path, bad: &mut bool) -> Features {
let mut features = Features::new();
- collect_lang_features_in(&mut features, base_compiler_path, "active.rs", bad);
collect_lang_features_in(&mut features, base_compiler_path, "accepted.rs", bad);
collect_lang_features_in(&mut features, base_compiler_path, "removed.rs", bad);
+ collect_lang_features_in(&mut features, base_compiler_path, "unstable.rs", bad);
features
}
@@ -336,11 +336,11 @@ fn collect_lang_features_in(features: &mut Features, base: &Path, file: &str, ba
let mut parts = line.split(',');
let level = match parts.next().map(|l| l.trim().trim_start_matches('(')) {
- Some("active") => Status::Unstable,
+ Some("unstable") => Status::Unstable,
Some("incomplete") => Status::Unstable,
Some("internal") => Status::Unstable,
Some("removed") => Status::Removed,
- Some("accepted") => Status::Stable,
+ Some("accepted") => Status::Accepted,
_ => continue,
};
let name = parts.next().unwrap().trim();
@@ -449,7 +449,7 @@ fn get_and_check_lib_features(
Ok((name, f)) => {
let mut check_features = |f: &Feature, list: &Features, display: &str| {
if let Some(ref s) = list.get(name) {
- if f.tracking_issue != s.tracking_issue && f.level != Status::Stable {
+ if f.tracking_issue != s.tracking_issue && f.level != Status::Accepted {
tidy_error!(
bad,
"{}:{}: `issue` \"{}\" mismatches the {} `issue` of \"{}\"",
@@ -566,7 +566,7 @@ fn map_lib_features(
let level = if line.contains("[unstable(") {
Status::Unstable
} else if line.contains("[stable(") {
- Status::Stable
+ Status::Accepted
} else {
continue;
};
@@ -581,7 +581,7 @@ fn map_lib_features(
Some(Err(_err)) => {
err!("malformed stability attribute: can't parse `since` key");
}
- None if level == Status::Stable => {
+ None if level == Status::Accepted => {
err!("malformed stability attribute: missing the `since` key");
}
None => None,
diff --git a/src/tools/tidy/src/lib.rs b/src/tools/tidy/src/lib.rs
index fc69c1432..eb0a2fda2 100644
--- a/src/tools/tidy/src/lib.rs
+++ b/src/tools/tidy/src/lib.rs
@@ -3,8 +3,6 @@
//! This library contains the tidy lints and exposes it
//! to be used by tools.
-use std::fmt::Display;
-
use termcolor::WriteColor;
/// A helper macro to `unwrap` a result except also print out details like:
@@ -31,16 +29,22 @@ macro_rules! t {
macro_rules! tidy_error {
($bad:expr, $($fmt:tt)*) => ({
- $crate::tidy_error($bad, format_args!($($fmt)*)).expect("failed to output error");
+ $crate::tidy_error(&format_args!($($fmt)*).to_string()).expect("failed to output error");
+ *$bad = true;
});
}
-fn tidy_error(bad: &mut bool, args: impl Display) -> std::io::Result<()> {
+macro_rules! tidy_error_ext {
+ ($tidy_error:path, $bad:expr, $($fmt:tt)*) => ({
+ $tidy_error(&format_args!($($fmt)*).to_string()).expect("failed to output error");
+ *$bad = true;
+ });
+}
+
+fn tidy_error(args: &str) -> std::io::Result<()> {
use std::io::Write;
use termcolor::{Color, ColorChoice, ColorSpec, StandardStream};
- *bad = true;
-
let mut stderr = StandardStream::stdout(ColorChoice::Auto);
stderr.set_color(ColorSpec::new().set_fg(Some(Color::Red)))?;
diff --git a/src/tools/tidy/src/mir_opt_tests.rs b/src/tools/tidy/src/mir_opt_tests.rs
index c307bcb93..76feeb343 100644
--- a/src/tools/tidy/src/mir_opt_tests.rs
+++ b/src/tools/tidy/src/mir_opt_tests.rs
@@ -26,7 +26,8 @@ fn check_unused_files(path: &Path, bless: bool, bad: &mut bool) {
for file in rs_files {
for bw in [32, 64] {
for ps in [PanicStrategy::Unwind, PanicStrategy::Abort] {
- for output_file in miropt_test_tools::files_for_miropt_test(&file, bw, ps) {
+ let mir_opt_test = miropt_test_tools::files_for_miropt_test(&file, bw, ps);
+ for output_file in mir_opt_test.files {
output_files.remove(&output_file.expected_file);
}
}
diff --git a/src/tools/tidy/src/style.rs b/src/tools/tidy/src/style.rs
index 11480e2be..cb40c6e3a 100644
--- a/src/tools/tidy/src/style.rs
+++ b/src/tools/tidy/src/style.rs
@@ -427,9 +427,12 @@ pub fn check(path: &Path, bad: &mut bool) {
"copyright notices attributed to the Rust Project Developers are deprecated"
);
}
- if is_unexplained_ignore(&extension, line) {
- err(UNEXPLAINED_IGNORE_DOCTEST_INFO);
+ if !file.components().any(|c| c.as_os_str() == "rustc_baked_icu_data") {
+ if is_unexplained_ignore(&extension, line) {
+ err(UNEXPLAINED_IGNORE_DOCTEST_INFO);
+ }
}
+
if filename.ends_with(".cpp") && line.contains("llvm_unreachable") {
err(LLVM_UNREACHABLE_INFO);
}
diff --git a/src/tools/tidy/src/ui_tests.rs b/src/tools/tidy/src/ui_tests.rs
index 2b828e58d..7e24793ad 100644
--- a/src/tools/tidy/src/ui_tests.rs
+++ b/src/tools/tidy/src/ui_tests.rs
@@ -11,7 +11,7 @@ use std::path::{Path, PathBuf};
const ENTRY_LIMIT: usize = 900;
// FIXME: The following limits should be reduced eventually.
const ISSUES_ENTRY_LIMIT: usize = 1854;
-const ROOT_ENTRY_LIMIT: usize = 865;
+const ROOT_ENTRY_LIMIT: usize = 867;
const EXPECTED_TEST_FILE_EXTENSIONS: &[&str] = &[
"rs", // test source files
diff --git a/src/version b/src/version
index 80627411d..7c7053aa2 100644
--- a/src/version
+++ b/src/version
@@ -1 +1 @@
-1.74.1
+1.75.0